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:
| 字段 | 处理要求 |
|---|---|
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 调用。
签名¶
请求格式¶
| 参数 | AIDL 类型 | 必填 | 规则 |
|---|---|---|---|
packageName |
String |
是 | 传当前应用真实 applicationId;服务会 trim(),空字符串无效 |
callback |
IEvccApiCallback |
是 | 客户端实现的回调 Binder;不能为 null,并应在会话期间保持强引用 |
服务端按以下身份链校验,不信任客户端单独提供的包名:
Binder.getCallingUid()必须只对应一个安装包;共享 UID 不受支持。packageName必须属于这个 UID。- EVCC 中必须存在该包名的授权记录。
- 当前 UID 和 APK 签名证书 SHA-256 必须与授权时记录一致。
- 授权修订号在会话创建期间不能发生变化。
同步返回¶
成功返回 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。