跳转至

IEvccApi 根接口

最后更新:

AIDL:com.kooo.evcc.evccapi.IEvccApi

契约状态 值
引入版本 EvccApi 1.0
最近接口变更 EvccApi 1.0
稳定性 稳定公开接口
弃用状态 未弃用

IEvccApi 是绑定 EVCC 后得到的根 Binder。它只负责协议握手和建立授权会话;属性、变量、动作和 Shell 都通过 IEvccApiSession 调用。

interface IEvccApi {
    EvccApiInfo getApiInfo();
    IEvccApiSession openSession(String packageName, IEvccApiCallback callback);
}

调用前提

客户端 Manifest 至少需要:

<uses-permission android:name="com.kooo.evcc.permission.ACCESS_EVCC_API" />

<queries>
    <package android:name="com.kooo.evcc" />
</queries>

绑定时使用显式 EVCC 包名,避免让系统解析到其他服务:

val intent = Intent(EvccApiContract.ACTION_BIND)
    .setPackage(EvccApiContract.EVCC_PACKAGE)

context.bindService(intent, connection, Context.BIND_AUTO_CREATE)

ACCESS_EVCC_API 是普通声明权限,不是最终授权。只有 openSession() 会校验当前调用应用在 EVCC 中的 授权记录。服务组件可能在云端关闭、EVCC 未激活或没有可用授权应用时处于禁用状态,此时绑定本身可能 失败。

getApiInfo()

读取服务端协议和 EVCC 版本。应在 openSession() 前调用,用 apiMajor 判断是否可以继续握手。

签名

EvccApiInfo getApiInfo();

请求

无参数。只需要成功绑定服务,不要求当前调用应用已经取得分类权限。

同步返回

EvccApiInfo:

字段 处理要求
apiMajor 必须与客户端支持的主版本相等,否则停止建会话
apiMinor 次版本允许增加兼容能力;具体功能仍以能力查询为准
contractHash 用于诊断所用契约基线,不能代替版本和能力判断
serviceVersionName EVCC 展示版本名
serviceVersionCode EVCC Android versionCode
capabilitiesRevision 能力目录修订号;只比较是否变化,不推导连续版本

没有异步回调。

异常与注意事项

  • EVCC 开放 API 在调用前被关闭时可抛出 SecurityException。
  • Binder 已死亡或传输失败时可抛出 RemoteException。
  • getApiInfo() 成功不代表应用已获业务授权;必须继续调用 openSession()。
  • 客户端应以 apiMajor 作为硬兼容边界,并在建会话后调用 queryCapabilities();不要只比较 contractHash 或 EVCC 版本名。

openSession(packageName, callback)

会话的 preset 可以为 ALL,表示用户选择了全部权限预设。判定具体能力始终使用 grantedPermissions,不得按预设名称推导权限,也不得自动申请或扩大用户授权。参见权限与应用授权。

校验调用应用身份和用户授权,为该客户端建立独立会话。成功后,后续业务方法只能由建立会话时的同一 UID 调用。

签名

IEvccApiSession openSession(String packageName, IEvccApiCallback callback);

请求格式

参数 AIDL 类型 必填 规则
packageName String 是 传当前应用真实 applicationId;服务会 trim(),空字符串无效
callback IEvccApiCallback 是 客户端实现的回调 Binder;不能为 null,并应在会话期间保持强引用

服务端按以下身份链校验,不信任客户端单独提供的包名:

  1. Binder.getCallingUid() 必须只对应一个安装包;共享 UID 不受支持。
  2. packageName 必须属于这个 UID。
  3. EVCC 中必须存在该包名的授权记录。
  4. 当前 UID 和 APK 签名证书 SHA-256 必须与授权时记录一致。
  5. 授权修订号在会话创建期间不能发生变化。

同步返回

成功返回 IEvccApiSession Binder。应立即调用 getSessionInfo(),读取实际权限集合和授权修订号。

该方法本身没有业务状态返回;身份或授权拒绝通过 SecurityException 表示。常见拒绝原因包括:

  • 包名或回调为空;
  • 调用 UID 与包名不匹配;
  • 使用共享 UID;
  • 应用未在 EVCC 中授权;
  • 应用重装后 UID 或签名变化;
  • 同一 UID 已存在 8 个会话;
  • EVCC 未激活、处于安全限制状态或云端关闭了开放 API。

回调关系

callback 成为这个会话的唯一服务端事件出口。其 Binder 死亡时 EVCC 会关闭会话并清理属性/变量订阅 及 Shell 任务。后续撤权、云端关闭或服务停止可通过 onSessionClosed() 通知客户端。

Raw AIDL 握手示例

以下代码应在工作线程运行;binder 来自 ServiceConnection.onServiceConnected():

val root = IEvccApi.Stub.asInterface(binder)
val apiInfo = root.apiInfo
require(apiInfo.apiMajor == EvccApiContract.API_MAJOR) {
    "Unsupported EVCC API ${apiInfo.apiMajor}.${apiInfo.apiMinor}"
}

val session = root.openSession(context.packageName, callback)
val sessionInfo = session.sessionInfo

注意事项

  • 不要传入别的包名尝试代理调用;包名不是可委托的身份字段。
  • 不要长期缓存旧会话。Binder 死亡或 SDK 重连后应使用新 IEvccApiSession。
  • 用户修改任一分类权限会增加授权修订号并关闭旧会话;客户端需要重新建立会话。
  • 推荐使用 EvccApiClient 处理绑定、主版本检查、Binder death 和退避重连;Raw AIDL 更适合调试和不使用 SDK 的客户端。

会话方法见IEvccApiSession,回调定义见 IEvccApiCallback。