跳转至

协议兼容与演进

最后更新:

EVCC 开放 API 的产品版本与 Binder 协议版本彼此独立。当前公开基线是 EVCC 3.3.1 中的 EvccApi 1.3:

apiMajor    = 1
apiMinor    = 3
contractHash = evccapi-1.3

客户端必须以运行时 getApiInfo() 的结果为准,不能仅按用户报告的 EVCC 展示版本推断接口能力。

握手顺序

推荐客户端按以下顺序建立会话:

  1. 绑定 com.kooo.evcc.evccapi.BIND。
  2. 调用 getApiInfo()。
  3. 检查 apiMajor == EvccApiContract.API_MAJOR;不相等则停止调用。
  4. 用真实应用包名和回调调用 openSession()。
  5. 读取 EvccSessionInfo,以 grantedPermissions 为本会话授权事实。
  6. 调用 queryCapabilities(),按能力 ID 判断当前版本、车型、来源和权限是否支持目标功能。

当前 EvccApiClient 自动完成前四步,并在主版本不一致时停止重试。contractHash 用于构建和排障时 确认双方契约基线;它不是授权凭据,也不能替代 queryCapabilities()。同一协议版本下,属性源在线 状态、车型支持和用户权限仍然可能不同。

版本字段如何使用

字段 客户端用途 不应做的事
apiMajor 判断 Binder/Parcelable 基线是否兼容 主版本不同仍尝试调用
apiMinor 判断某个后续追加能力是否可能存在 只看次版本、不查询能力
contractHash 日志、产物核对和异常组合诊断 作为身份或签名校验
serviceVersionName/code 展示 EVCC 版本、提交问题时记录 用版本名字符串比较能力
capabilitiesRevision 发现内置能力目录可能变化后重新查询 当作时间戳或用户授权版本
grantRevision 发现当前应用授权记录变化 假设修订号连续或跨设备可比

应用应在每次 onSessionReady() 后重新读取会话权限和能力。来源上下线不会要求协议升级,应通过 querySourceStates() 和 onSourceStateChanged() 处理;来源代次语义见 初始化、监听与重同步。

1.x 兼容规则

协议 1.x 采用保守的追加式演进。以下变化可以在次版本中出现,但新客户端必须同时使用 apiMinor、 能力查询或业务状态进行保护:

  • 新增能力 ID、权限 ID、动作 ID、明确来源 ID或状态字符串;客户端应忽略未知 ID,并把未知状态按 “非成功、保留原文供诊断”处理。
  • 在 AIDL 接口末尾追加使用新请求/返回类型的方法;已有方法的事务顺序和签名保持不变。新客户端 连接旧服务时,不得在确认次版本/能力前调用新增方法。
  • 新增独立 Parcelable 类型,而不是改变旧类型的线格式。
  • 收紧资源限制或安全策略时,通过明确状态和 message 返回;客户端不能把当前上限当成永久配额。

当前 Parcelable 使用按固定顺序手写的无版本长度 Parcel 编码。因此,现有类型在 1.x 中必须保持:

  • 字段数量、顺序和基础类型不变;
  • nullable 与非 nullable 约定不变;
  • 默认值和既有字段语义不被重新解释;
  • 时间字段的单位/时基不被静默改变。

需要增加模型信息时,优先新增类型和新方法,或把可扩展内容放在有独立 schema 的 JSON 字段中。只有 “在现有 Parcelable 末尾再写一个字段”并不能保证旧客户端向新服务端发送请求时仍可安全解码。

以下变化需要提升主版本,而不能伪装成 1.x 小改动:

  • 删除、重命名或改变现有 AIDL 方法,或在已有方法中间插入导致事务编号变化的方法;
  • 改变 Parcelable 字段顺序、类型、可空性或编码方式;
  • 改变现有状态、来源、权限或动作 ID 的含义;
  • 把同步调用改为异步回调,或反向改变;
  • 改变既有来源之间的 AUTO 相对优先级、写入不回退等会影响控制目标的基础规则;精确车型门控的新来源可在次版本中追加,但必须通过 resolvedSource 和能力查询可见;
  • 为 observedAt 等既有字段静默指定与 1.0 不兼容的新单位。

能力兼容优先于版本分支

版本只能说明“某项能力可能存在”,不能说明当前应用可以使用。正确模式是按稳定 ID 查找:

val capabilities = withContext(Dispatchers.IO) {
    client.queryCapabilities().associateBy(EvccCapability::id)
}

val canWrite = capabilities[EvccApiContract.Permission.PROPERTY_WRITE]?.available == true
val canLaunch = capabilities["action.app.launch"]?.available == true

权限能力、source.* 能力和 action.* 能力共享 EvccCapability 模型,但 ID 空间不同。执行动作时传入 不带 action. 前缀的动作 ID,详见变量与动作。任何高风险能力仍须以服务端 逐次权限检查为准,能力查询结果不能缓存成永久授权。

升级、降级与重装

  • EVCC 升级或进程重启会结束现有 Binder 会话;SDK 重连后重新读取 EvccApiInfo、授权和能力,并恢复 最后一次成功的订阅集合。
  • 客户端升级后必须保留对其声明支持的最低协议次版本的分支;不能因为编译时 Contract 更新就直接调用 新方法。
  • EVCC 降级后若主版本仍相同,客户端仍应按实际次版本和能力运行;缺失能力应降级界面,而不是循环 调用。
  • 第三方应用重新签名、改变 UID 或使用共享 UID 时,旧授权不会继承。它属于身份/授权变化,不是协议 兼容问题;处理方式见权限与授权。
  • “米德宝/AIDL 联动”旧版接口与 EVCC 开放 API 是独立协议和授权域,版本号不能互相比较,也不存在 自动迁移。

发布前兼容检查

第三方应用至少应验证:

  1. 当前 1.3 EVCC 正常连接、读取和取消订阅;1.0 客户端只使用已有事务。
  2. EVCC 被停止再恢复后,SDK 重绑且业务以新 INITIAL_SNAPSHOT 建立基线。
  3. 撤权、功能关闭和签名变化时停止重试并给出用户可操作提示。
  4. 未知能力和未知非成功状态不会造成崩溃或误判成功。
  5. 目录始终按 nextPageToken + snapshotToken 继续,不依赖固定每页数量。
  6. 写入和动作在能力不足、权限不足或车型不支持时安全降级。

精确 AIDL 方法见AIDL 接口参考,全部 Parcelable 字段见 数据模型参考,通用错误处理见状态与错误码。

1.1 音乐扩展

会话尾部追加 5 个音乐方法,回调尾部追加音乐事件,原 1.0 Parcelable 不变。新客户端先检查 minor>=1,旧客户端不订阅音乐时不会收到新回调。详情见音乐与歌词。

1.2 音量组扩展

会话尾部追加 4 个音量方法,新增独立请求和返回模型;已有模型、方法和回调不变。新 SDK 连接 1.0/1.1 服务时不发送音量事务,返回 UNSUPPORTED。权限和车型支持仍需分别判断;重连不重放写入。见音量组。

1.4 ECARX 原生 VHAL 来源

新增稳定来源 ID ECARX_VHAL,不追加 AIDL 事务,也不改变任何 Parcelable 布局。该来源受精确车型和云端 profile 双重门控,仅 L946 配置会进入 AUTO 首位;不满足门槛时它是不可用来源,不改变其他车型的实际解析结果。旧客户端会按 1.x 规则忽略未知来源 ID;需要显式选择该来源的客户端应先确认 apiMinor >= 4 并查询 source.ecarx_vhal 能力。旧 ECARX_EAS ID 不作为别名保留。