IEvccApiSession 会话接口¶
最后更新:
AIDL:
com.kooo.evcc.evccapi.IEvccApiSession
| 契约状态 | 值 |
|---|---|
| 引入版本 | EvccApi 1.0 |
| 最近接口变更 | EvccApi 1.2 |
| 稳定性 | 稳定公开接口 |
| 弃用状态 | 未弃用 |
IEvccApiSession 是应用通过 IEvccApi.openSession() 获得的授权会话。每个会话固定绑定建立它的 UID、
包名、签名和授权修订号;撤权、换签、云端关闭或 Binder 断开都会使旧会话失效。
本接口的所有方法都是同步 Binder 调用。除 getSessionInfo()、queryCapabilities() 和 close() 等轻量
调用外,统一放到工作线程执行。业务失败写入返回模型的 status;Binder 传输失败另行捕获
RemoteException。
完整签名¶
interface IEvccApiSession {
EvccSessionInfo getSessionInfo();
List<EvccCapability> queryCapabilities();
EvccPropertyRecord resolveProperty(in EvccPropertySelector selector);
EvccPropertyRecord readProperty(in EvccPropertySelector selector, boolean forceRefresh);
List<EvccPropertyRecord> readProperties(
in List<EvccPropertySelector> selectors,
boolean forceRefresh
);
EvccOperationResult replaceSubscriptions(in List<EvccPropertySelector> selectors);
EvccPropertyPage queryPropertyDirectory(in EvccPageRequest request);
List<EvccSourceState> querySourceStates();
EvccOperationResult writeProperty(in EvccWriteRequest request);
List<EvccOperationResult> writeProperties(in List<EvccWriteRequest> requests);
List<EvccVariableRecord> queryVariables();
EvccVariableRecord readVariable(String name);
EvccOperationResult replaceVariableSubscriptions(in List<String> names);
EvccOperationResult writeVariable(in EvccVariableWriteRequest request);
EvccOperationResult deleteVariable(String name);
EvccOperationResult executeAction(in EvccActionRequest request);
EvccOperationResult executeShell(in EvccShellRequest request);
EvccOperationResult cancel(String requestId);
void close();
// API 1.1
EvccMusicState readMusicState();
EvccOperationResult setMusicSubscription(boolean enabled);
EvccMusicResource openMusicLyrics(String trackId, long revision);
EvccMusicResource openMusicArtwork(String trackId, long revision);
EvccOperationResult sendMusicCommand(in EvccMusicCommand request);
// API 1.2
EvccVolumeGroupsResult queryVolumeGroups();
EvccVolumeGroupState readVolumeGroup(int groupId);
EvccVolumeWriteResult setVolumeGroup(in EvccVolumeSetRequest request);
EvccVolumeWriteResult adjustVolumeGroup(in EvccVolumeAdjustRequest request);
}
通用返回规则¶
EvccOperationResult.isSuccess只在OK或ACCEPTED时为true。Shell 等待确认时返回的PENDING不属于这个快捷判断。message是诊断文本,不作为稳定分支条件。- 记录型返回先检查外层
status,再读取descriptor、value或items。 - 单条或整批结果超过 Binder 安全预算时,服务会裁剪值或返回
PAYLOAD_TOO_LARGE。字段定义见 数据模型参考。
getSessionInfo()¶
用途:读取当前会话身份、实际授权权限和授权修订号。
调用属性与权限:同步、轻量;只要求当前调用 UID 与有效会话一致,不需要分类权限。
请求:无参数。
同步返回:EvccSessionInfo。程序应以 grantedPermissions 为准,不能仅根据 preset 推断权限。
正常返回包含 sessionId、已验证的 clientPackage、权限 ID 列表、授权预设、grantRevision 和会话创建
时间。没有异步回调。
异常/状态:本类型没有 status 字段。授权在调用时失效,服务会关闭会话;返回值可能不再包含有效
身份。Binder 断开则抛出 IPC 异常。
val info = session.sessionInfo
if (EvccApiContract.Permission.PROPERTY_READ !in info.grantedPermissions) {
// 提示用户在 EVCC 中授予属性读取权限。
}
注意事项:grantRevision 只用于判断授权是否变化。权限变化后旧会话会关闭,不能在旧 Binder 上继续
使用新的权限集合。
queryCapabilities()¶
用途:发现当前会话实际可用的权限、属性源和动作能力。
调用属性与权限:同步、轻量;只要求有效会话,不需要分类权限。
请求:无参数。
同步返回:List<EvccCapability>,列表包含三类 ID:
| ID 形式 | 示例 | available 的含义 |
|---|---|---|
| 权限 ID | property.read |
当前会话是否已获该权限 |
| 来源能力 | source.vhal、source.apvp |
当前来源是否不是 UNAVAILABLE |
| 动作能力 | action.app.launch |
当前动作存在且会话具有其权限 |
没有异步回调。列表顺序不是协议。
异常/状态:会话失效时返回空列表或发生 Binder 异常;单个能力不可用通过
EvccCapability.available = false 表示,不是方法整体失败。
val capabilities = session.queryCapabilities()
val canRead = capabilities.firstOrNull { it.id == "property.read" }?.available == true
注意事项:不要把 EVCC 版本号或授权预设当作能力判断。reason 用于展示,id 和 available 才是
程序判断字段。
resolveProperty(selector)¶
用途:只解析属性身份,确定实际来源、规范 ID、区域和候选项,不读取当前值。
调用属性与权限:同步;需要 property.directory。服务等待上限约 15 秒,并受每 UID 4 个、全局
12 个阻塞调用并发限制。
请求格式:
| 参数 | 类型 | 规则 |
|---|---|---|
selector |
EvccPropertySelector |
不能为 null;id 与精确 name 至少提供一个 |
id 必须在无符号 32 位范围内。source 可为 AUTO 或明确来源;areaId 为空时使用自动区域规则。
字段及解析模式见属性模型与读写。
同步返回:EvccPropertyRecord。成功时 status = OK,descriptor 和 resolvedSource 有效,value
通常为空;发生来源或区域歧义时从 candidates 读取候选项。没有异步回调。
可能状态:OK、INVALID_ARGUMENT、NOT_FOUND、AMBIGUOUS_SOURCE、AMBIGUOUS_AREA、
PERMISSION_DENIED、RATE_LIMITED、TIMEOUT、PAYLOAD_TOO_LARGE。
val record = session.resolveProperty(
EvccPropertySelector(name = "VehicleSpeed")
)
if (record.status == EvccApiContract.Status.OK) {
val source = record.resolvedSource
val area = record.descriptor?.areaId
}
注意事项:AUTO 按 VHAL → APVP → VEHICLE_AIDL → CAPI → ADAPT_API → SOA 解析。ALL_MATCHES 也不会
改变本方法的单记录返回类型;需要客户端选择时,候选放在 candidates 中。
readProperty(selector, forceRefresh)¶
用途:解析并读取一个属性的当前类型化值。
调用属性与权限:同步;需要 property.read。服务等待上限约 15 秒,受阻塞调用并发限制。
请求格式:
| 参数 | 类型 | 规则 |
|---|---|---|
selector |
EvccPropertySelector |
同 resolveProperty() |
forceRefresh |
boolean |
false 优先使用已初始化缓存;true 要求访问选定上游刷新 |
同步返回:EvccPropertyRecord。先检查 record.status,成功后再按 record.value.type 读取对应的
类型字段。value.initialized = false 表示尚未取得真实初值,不能把空文本当作属性值。没有异步回调。
可能状态:OK、PENDING、UNAVAILABLE、NOT_FOUND、AMBIGUOUS_SOURCE、
AMBIGUOUS_AREA、INVALID_ARGUMENT、PERMISSION_DENIED、RATE_LIMITED、TIMEOUT、
PAYLOAD_TOO_LARGE。
val record = session.readProperty(
EvccPropertySelector(id = 557_849_089L),
false,
)
val value = record.value
if (record.status == EvccApiContract.Status.OK && value?.initialized == true) {
// 根据 value.type 读取 longValue、doubleValue、booleanValue 或 stringValue。
}
注意事项:强制刷新会增加上游请求,不应作为高频轮询方式。普通读取在无初值、来源换代或明确要求 刷新时才访问上游;需要持续更新时使用订阅。
readProperties(selectors, forceRefresh)¶
用途:一次读取多个属性,减少 Binder 往返。
List<EvccPropertyRecord> readProperties(
in List<EvccPropertySelector> selectors,
boolean forceRefresh
);
调用属性与权限:同步;需要 property.read。最多 100 项,整批等待上限约 30 秒,受阻塞调用并发
限制。
请求格式:
| 参数 | 类型 | 规则 |
|---|---|---|
selectors |
List<EvccPropertySelector> |
null 按空列表处理;最多 100 项 |
forceRefresh |
boolean |
对本批所有选择器使用同一缓存/刷新策略 |
同步返回:正常执行时与请求顺序一致,每个选择器对应一个 EvccPropertyRecord。如果整批在进入执行
前被拒绝、限流或超时,服务可能只返回一个没有对应选择器的失败记录;因此不能无条件按下标映射。没有
异步回调。
如果整批结果超过 Binder 安全预算,服务返回能够容纳的有序前缀,并在末尾追加一个
status = PAYLOAD_TOO_LARGE、没有对应选择器的哨兵记录。该记录表示后续项目被省略,不是原请求中的
一个属性。
可能状态:每项与 readProperty() 相同;整批还可能以单条 INVALID_ARGUMENT、
PERMISSION_DENIED、RATE_LIMITED 或 TIMEOUT 失败。
val records = session.readProperties(
listOf(
EvccPropertySelector(id = 557_849_089L),
EvccPropertySelector(name = "VehicleSpeed"),
),
false,
)
注意事项:本方法不是快照事务,不保证不同来源的值来自同一时刻。遇到
PAYLOAD_TOO_LARGE 时缩小批次;不要原样重试同一超大请求。
replaceSubscriptions(selectors)¶
用途:用一个新集合原子替换本会话全部属性监听需求,并为每个成功项产生初始快照。
调用属性与权限:同步提交;同时需要 property.listen 和 property.read。最多保留 256 个物理监听,
初始化等待上限约 30 秒,受阻塞调用并发限制。
请求格式:
| 参数 | 类型 | 规则 |
|---|---|---|
selectors |
List<EvccPropertySelector> |
null 或空列表清除监听;重复物理目标会去重 |
同步返回:EvccOperationResult。
OK:全部成功;acceptedCount是实际接受数。ACCEPTED:至少一项成功但存在失败或超过 256 项;检查acceptedCount、failedCount和message。FAILED:没有任何选择器成功解析。
异步回调:每个成功目标先通过 onPropertyEvent(..., INITIAL_SNAPSHOT) 给出基线,之后发送
VALUE_CHANGED;来源状态变化通过 onSourceStateChanged(),AUTO 来源切换使用 SOURCE_CHANGED。
回调是 oneway,可能在调用线程处理同步返回前已经进入客户端队列。
可能状态:OK、ACCEPTED、FAILED、PERMISSION_DENIED、RATE_LIMITED、TIMEOUT、
SESSION_CLOSED。
val result = session.replaceSubscriptions(
listOf(EvccPropertySelector(id = 557_849_089L))
)
if (!result.isSuccess) {
// 订阅集合没有成功替换,保留当前业务基线并处理状态。
}
注意事项:这是“整集合替换”,不是追加。空列表用于释放需求。AUTO 目标解析后会粘滞于实际来源; 瞬时断线先等待恢复,只有来源关闭或持续不支持时才尝试切换。SDK 会在重连和重同步时重放最后一次成功 提交的集合。
queryPropertyDirectory(request)¶
用途:分页查询属性目录;可选返回当前值,或显式发起高成本实时全量扫描。
调用属性与权限:同步;基础权限为 property.directory。includeValues = true 另需
property.read;mode = LIVE_FULL_SCAN 另需独立授权 property.full_scan。服务调用等待上限约 20 秒。
请求格式:
| 字段 | 规则 |
|---|---|
requestId |
调用方关联 ID |
pageToken |
第一页为空;后续使用上页 nextPageToken,最长 32 字符 |
snapshotToken |
第一页为空;续页必须回传首页面令牌,最长 128 字符 |
source |
AUTO 表示全部已开启来源,或指定一个来源;最长 64 字符 |
query |
按属性目录文本筛选,最长 512 字符 |
limit |
1~500;Raw AIDL 超界值会被服务夹到范围内,SDK 会在本地拒绝 |
includeValues |
是否包含 EvccValue |
mode |
DIRECTORY 或 LIVE_FULL_SCAN |
同步返回:EvccPropertyPage。status = OK 时消费 items;只以
nextPageToken.isEmpty() 判断结束。后续页必须保持 source、query、mode 和 includeValues 不变。
没有异步回调。
可能状态:OK、INVALID_ARGUMENT、PERMISSION_DENIED、RATE_LIMITED、TIMEOUT、
PAYLOAD_TOO_LARGE。失败页通过页级 status/message 表示,不要只检查 items 是否为空。
var pageToken = ""
var snapshotToken = ""
do {
val page = session.queryPropertyDirectory(
EvccPageRequest(
requestId = "directory-1",
pageToken = pageToken,
snapshotToken = snapshotToken,
source = EvccApiContract.Source.AUTO,
limit = 100,
)
)
check(page.status == EvccApiContract.Status.OK)
if (snapshotToken.isEmpty()) snapshotToken = page.snapshotToken
page.items.forEach { /* 使用 descriptor */ }
pageToken = page.nextPageToken
} while (pageToken.isNotEmpty())
注意事项:稳定目录快照约保留 60 秒,但可能因资源预算提前回收;令牌过期后从第一页重查。服务可
因 Binder 字节预算返回少于 limit 的项目。LIVE_FULL_SCAN 最长约 15 秒、同一应用每 30 秒最多
发起一次,并可能临时扩大 APVP 订阅;并发扫描会合并,成功结果短期缓存约 10 秒。
querySourceStates()¶
用途:查询五个属性源当前是否启用、是否已连接及连接代次。
调用属性与权限:同步、轻量;需要 property.directory。
请求:无参数。
同步返回:List<EvccSourceState>。每项包含 source、state、展示消息、sourceGeneration 和
生成时间。允许时当前实现按 VHAL、APVP、车辆 AIDL、CAPI、AdaptAPI、SOA 顺序返回,但客户端不应依赖顺序。
没有异步回调。
异常/状态:没有页级状态;权限不足或会话失效时返回空列表。来源自己的状态通常为 AVAILABLE、
PENDING 或 UNAVAILABLE。
注意事项:PENDING 可表示来源已开启但尚未连接;本方法不会要求第三方管理来源生命周期。持续监听
期间的相关状态变化通过 onSourceStateChanged() 发送。
writeProperty(request)¶
用途:解析并写入一个可写属性,或仅预检查权限、目标和写入能力。
调用属性与权限:同步;需要 property.write。服务等待上限约 15 秒,受阻塞调用并发限制。实际写入
按 UID 令牌桶限流:可突发 100 项,之后约每秒恢复 10 项;validateOnly 不计费。
请求格式:
| 字段 | 规则 |
|---|---|
requestId |
建议使用唯一 ID;返回时最多保留 128 字符 |
selector |
写入目标;AUTO 允许选择来源 |
value |
STRING/LONG/DOUBLE/BOOLEAN/JSON/BYTES_BASE64 之一,类型字段必须一致 |
validateOnly |
true 只检查,不产生上游写入 |
规范化后的写入文本最长 16,384 字符。NULL 不能作为写入类型。
同步返回:EvccOperationResult。OK 表示上游已接受/发送写入,不保证车辆执行结果会有单独回执;
本方法没有异步完成回调。
可能状态:OK、INVALID_ARGUMENT、PERMISSION_DENIED、RATE_LIMITED、TIMEOUT、
NOT_FOUND、AMBIGUOUS_SOURCE、AMBIGUOUS_AREA、UNAVAILABLE、UNSUPPORTED、FAILED。
val result = session.writeProperty(
EvccWriteRequest(
requestId = "write-speed-mode-1",
selector = EvccPropertySelector(id = 0x1234L),
value = EvccValue.ofLong(1L),
validateOnly = true,
)
)
注意事项:建议先以 validateOnly = true 预检查,再由用户明确触发实际写入。AUTO 一旦选定实际来源,
写入失败不会向下一来源重放,避免同一操作被执行两次。
writeProperties(requests)¶
用途:按顺序提交多个属性写入。
调用属性与权限:同步;需要 property.write。最多 100 项,整批等待上限约 30 秒,受阻塞调用和属性
写入令牌桶限制。配额按本批 validateOnly = false 的项目数在执行前扣取。
请求格式:requests 为有序 EvccWriteRequest 列表;null 按空列表处理。
同步返回:通常每个已执行请求对应一个结果并保持顺序。以下情况会出现单个或尾部汇总结果:
- 整批超限、无权限或被限流:返回一个失败结果;
- 执行期间撤权:已执行项保留,末尾结果说明剩余项未执行;
- 达到总超时:已执行项保留,末尾
TIMEOUT说明后续项未执行。
没有异步回调。
可能状态:逐项与 writeProperty() 相同;整批还可能出现汇总的 INVALID_ARGUMENT、
PERMISSION_DENIED、RATE_LIMITED 或 TIMEOUT。
val results = session.writeProperties(requests)
results.forEach { result ->
// 用 requestId 关联;不要只按返回下标判断原请求。
}
注意事项:批量写入明确不是事务,没有回滚。前一项成功、后一项失败时,前一项不会撤销;也不会把 同一 AUTO 写入改投其他来源。
queryVariables()¶
用途:读取 EVCC 当前公开的全部用户变量和内置变量。
调用属性与权限:同步;需要 variable.read。
请求:无参数。
同步返回:按变量名排序的 List<EvccVariableRecord>。存在的变量通常为 status = OK;读取每项时
还应检查 mutable,内置变量为只读。没有异步回调。
如果完整列表超过 Binder 预算,末尾会加入名称为 __evccapi_truncated__、状态为
PAYLOAD_TOO_LARGE 的哨兵记录,表示结果不完整。权限不足时返回仅含一个 PERMISSION_DENIED 记录的
列表。
val variables = session.queryVariables()
val truncated = variables.any {
it.name == "__evccapi_truncated__" &&
it.status == EvccApiContract.Status.PAYLOAD_TOO_LARGE
}
注意事项:变量值统一通过 EvccValue 返回;当前变量运行时由自动化、Lua 与开放 API 共用。不要把
查询结果中的展示文本作为变量类型协议。
readVariable(name)¶
用途:读取一个指定变量。
调用属性与权限:同步;需要 variable.read。
请求格式:
| 参数 | 类型 | 规则 |
|---|---|---|
name |
String |
服务会去除首尾空白;必须匹配 [A-Za-z_][A-Za-z0-9_]{0,127} |
同步返回:EvccVariableRecord。OK 表示存在,NOT_FOUND 表示名称合法但变量不存在。没有异步
回调。
可能状态:OK、NOT_FOUND、INVALID_ARGUMENT、PERMISSION_DENIED、
PAYLOAD_TOO_LARGE。
val record = session.readVariable("drive_mode")
if (record.status == EvccApiContract.Status.OK) {
val text = record.value?.stringValue
}
注意事项:mutable = false 表示该变量不能通过开放 API 写入或删除;读取权限并不隐含写权限。
replaceVariableSubscriptions(names)¶
用途:用一个新集合替换本会话全部变量监听,并为每个名称发送初始快照。
调用属性与权限:同步提交;同时需要 variable.listen 和 variable.read。去重后最多 256 个名称。
请求格式:names 中每个名称会去除首尾空白,且必须匹配变量名规则。null 或空列表清除全部变量
监听。Raw AIDL 中空字符串会导致整次请求 INVALID_ARGUMENT;Client SDK 会先过滤空字符串。
同步返回:EvccOperationResult。OK 时 acceptedCount 为去重后的名称数。不存在的合法名称也可
订阅,其初始记录为 NOT_FOUND。订阅名单包含无效名称或超过 256 项时整次请求失败,不部分接受。
异步回调:每个名称先通过 onVariableEvent(..., INITIAL_SNAPSHOT) 发送基线,之后发送
VALUE_CHANGED 或 REMOVED。回调可能在调用方处理同步返回前进入客户端队列。
可能状态:OK、INVALID_ARGUMENT、PERMISSION_DENIED、SESSION_CLOSED。
注意事项:这是整集合替换,不是追加。用户变量通过运行时事件通知;时间、GPS、音乐状态等内置变量 由 EVCC 在存在监听需求时按约 1 秒间隔检查,第三方应用不需要管理其上游生命周期。
writeVariable(request)¶
用途:创建或更新一个用户变量,或只执行预检查。
调用属性与权限:同步;需要 variable.write。
请求格式:
| 字段 | 规则 |
|---|---|
requestId |
建议使用唯一关联 ID;返回最多保留 128 字符 |
name |
合法变量名,且不能是 EVCC 内置只读变量 |
value |
类型字段必须一致;服务转换为共用运行时的规范文本值 |
validateOnly |
true 只校验,不修改变量或消耗变更次数 |
单值最长 16,384 字符且 UTF-8 最大 64 KiB。公开 API 最多保留 512 个用户变量,总量最大约 2 MiB;如果 自动化/Lua 现有数据已超过预算,API 只允许保持或缩小占用。
同步返回:EvccOperationResult。OK 表示预检查通过或变量已经写入。没有单独完成回调;已监听该
名称的会话会收到变量变化事件。
可能状态:OK、INVALID_ARGUMENT、PERMISSION_DENIED、RATE_LIMITED、
PAYLOAD_TOO_LARGE。实际写入和删除共享每 UID 每分钟 120 次变更限额。
val result = session.writeVariable(
EvccVariableWriteRequest(
requestId = "variable-1",
name = "drive_mode",
value = EvccValue.ofString("sport"),
)
)
注意事项:validateOnly 不能预留名称或容量。预检查成功后到实际写入前,授权、配额或全局变量占用
仍可能变化,实际调用必须再次处理返回状态。
deleteVariable(name)¶
用途:删除一个用户变量。
调用属性与权限:同步;需要 variable.delete。与变量写入共享每 UID 每分钟 120 次变更限额。
请求格式:name 会去除首尾空白,必须匹配变量名规则,且不能是内置只读变量。
同步返回:EvccOperationResult。OK 表示已删除,NOT_FOUND 表示变量不存在。此方法没有
requestId 参数,所以返回的 requestId 为空。没有单独完成回调;已监听该名称的会话会收到
REMOVED。
可能状态:OK、NOT_FOUND、INVALID_ARGUMENT、PERMISSION_DENIED、RATE_LIMITED。
注意事项:删除会影响同一 EVCC 进程内使用该用户变量的自动化、Lua 和其他已授权 API 客户端,调用 前应确认变量所有权和业务影响。
executeAction(request)¶
用途:执行 EVCC 明确列入能力目录的安全动作或高权限动作。
调用属性与权限:同步。权限由 actionId 决定。实际执行按 UID 令牌桶限流:可突发 10 次,之后约
每秒恢复 2 次;validateOnly 不计费。
请求格式:
| 字段 | 规则 |
|---|---|
requestId |
最长 128 字符,不能包含控制字符 |
actionId |
最长 128 字符,不能包含空白/控制字符 |
argumentsJson |
必须是 JSON 对象文本,最长 64 KiB |
validateOnly |
true 只做动作存在性、权限与参数校验 |
API 1.0 当前动作:
actionId |
所需权限 | argumentsJson |
结果 |
|---|---|---|---|
app.launch |
action.safe |
{"packageName":"com.example.app"} |
启动目标应用的 Launcher Activity |
home.show |
action.safe |
{} |
返回系统桌面 |
toast.show |
action.safe |
{"text":"hello"} |
显示短提示;展示文本最多使用前 500 字符 |
automation.trigger |
action.privileged |
{"ruleId":"..."} |
触发当前正在运行的自动化规则 |
packageName 必须是合法的点分 Android 包名;packageName 和 ruleId 都必须非空且最长 512 字符。
toast.show 的 text 必须非空,实际展示最多取前 500 字符。未列出的额外 JSON 字段不构成稳定协议。
同步返回:EvccOperationResult。OK 表示动作已经同步提交/执行;没有异步完成回调。
可能状态:OK、UNSUPPORTED、INVALID_ARGUMENT、PAYLOAD_TOO_LARGE、
PERMISSION_DENIED、RATE_LIMITED、FAILED。
val result = session.executeAction(
EvccActionRequest(
requestId = "launch-1",
actionId = "app.launch",
argumentsJson = """{"packageName":"com.example.app"}""",
validateOnly = false,
)
)
注意事项:先通过 queryCapabilities() 检查 action.<actionId>。动作列表是显式白名单;不能把任意
自动化动作名直接当作 actionId。automation.trigger 还要求目标规则正在运行且 ID 存在。
executeShell(request)¶
用途:提交通过 EVCC 安全策略检查的自由输入 Shell 命令。该方法采用“同步接收 + 异步完成”两阶段 模型。
调用属性与权限:同步提交;始终需要独立授权 shell.exec。被判定为系统设置、应用状态或系统服务
操作的命令还需要 shell.system_write,并由车机端逐次确认。同一 UID 在所有会话间同时只能有一个待
确认或执行中的 Shell 请求。
请求格式:
| 字段 | 规则 |
|---|---|
requestId |
必填、当前会话任务中唯一、最长 128 字符且不能含控制字符 |
command |
必填,最长 4096 字符;服务会规范化并逐段检查 |
timeoutMs |
服务夹到 1,000~30,000 ms;模型默认 10,000 ms |
includeRawOutput |
1.0 预留字段;当前不改变返回结构,输出始终受授权和大小限制 |
同步返回:
| 状态 | 含义 | 下一步 |
|---|---|---|
ACCEPTED |
命令已进入执行 | 等待 onShellCompleted() |
PENDING |
正等待车机端系统写入确认 | 等待确认及最终回调,或调用 cancel() |
| 其他失败状态 | 未进入可执行流程 | 按状态修正请求或权限,不等待成功结果 |
EvccOperationResult.isSuccess 对 PENDING 为 false,因此 Shell 调用必须显式处理该状态。
异步回调:最终结果通过 onShellCompleted(EvccShellResult) 返回,用 requestId 关联。结果包含最终
状态、退出码、stdout、stderr、是否裁剪及耗时。stdout 与 stderr 原始合计最多 256 KiB,并会再按
Binder 预算裁剪。
可能同步状态:ACCEPTED、PENDING、INVALID_ARGUMENT、PERMISSION_DENIED、
RATE_LIMITED、FAILED。最终回调常见 OK、FAILED、CANCELLED、PERMISSION_DENIED。
val requestId = "shell-${System.currentTimeMillis()}"
val accepted = session.executeShell(
EvccShellRequest(
requestId = requestId,
command = "getprop ro.build.version.release",
timeoutMs = 10_000,
)
)
// ACCEPTED 或 PENDING 都不是最终执行结果;在回调中按 requestId 接收结果。
注意事项:公开 Shell 永久拒绝命令替换、变量展开、解释器二次执行、here-doc、通配展开、后台任务、 通用文件修改、进程终止、磁盘/分区/启动链操作、持久提权和资源耗尽等高风险形式。不要尝试编码或拼接 绕过;完整边界见Shell 安全边界。系统写入确认 30 秒内未处理会取消。
cancel(requestId)¶
用途:取消本会话提交的待确认或正在执行的 Shell 请求。
调用属性与权限:同步、轻量;只要求会话身份和授权仍有效,不额外要求 Shell 分类权限。若授权整体 已经变化,旧会话会关闭,并由服务端清理待确认或执行中的任务。
请求格式:requestId 会去除首尾空白,应与 executeShell() 的 ID 完全一致。
同步返回:
CANCELLED:待确认请求已取消,或运行中任务已收到取消请求;NOT_FOUND:当前会话没有这个可取消请求;PERMISSION_DENIED:会话身份或授权已失效。
运行中的任务在回调通道仍有效时通常还会发送最终 onShellCompleted(..., CANCELLED);Binder 已断开或
会话已关闭时不能依赖回调到达。
注意事项:CANCELLED 表示取消已经受理,不是对车机所有外部副作用的事务回滚。命令在收到取消前
已经完成的部分无法撤销。
close()¶
用途:由客户端主动关闭会话并释放服务端资源。
调用属性与权限:同步、轻量;只能由建立会话的 UID 调用,无分类权限。
请求/返回:无参数、无返回值。服务端移除属性、变量和音乐订阅,取消待确认/运行中的 Shell,解除 callback
death 监听并释放会话。主动关闭不会再向同一回调发送 onSessionClosed()。
异常与注意事项:仍需处理 Binder 已死亡时的 RemoteException。close() 后所有旧会话方法都应
视为无效;需要继续调用时重新绑定并通过根接口建立新会话。使用 EvccApiClient 时优先调用 SDK 的
disconnect() 或终态 close(),不要绕过 SDK 单独关闭其底层会话。
回调的线程、事件序号和重同步要求见IEvccApiCallback。
API 1.1 音乐方法¶
| 方法 | 返回 | 权限 |
|---|---|---|
readMusicState() |
EvccMusicState |
music.read |
setMusicSubscription(boolean enabled) |
EvccOperationResult |
开启需要 music.listen + music.read;关闭仅需有效会话 |
openMusicLyrics(String trackId, long revision) |
EvccMusicResource |
music.read |
openMusicArtwork(String trackId, long revision) |
EvccMusicResource |
music.read |
sendMusicCommand(in EvccMusicCommand request) |
EvccOperationResult |
music.control |
均为同步 Binder 调用,应在工作线程执行。完整资源、控制、订阅与错误语义见音乐专题。
API 1.2 音量组方法¶
queryVolumeGroups() 和 readVolumeGroup(groupId) 需要 volume.read,成功返回 AVAILABLE。setVolumeGroup(request) 和 adjustVolumeGroup(request) 需要 volume.control,下发成功或预检查通过返回 OK。四者均无额外回调,均使用工作线程。完整请求、返回、配额和不确定结果处理见音量组。