本文档是屏幕自动化操作声明协议(SAEP,Screen Automation Execution Protocol)的官方接入指南,旨在帮助 Android 应用开发者理解并接入屏幕自动化操作声明功能。开发者可通过本指南完成策略声明、能力检测、Agent 信息查询和操作日志审计,在支持自动化协作的同时保护应用数据、业务资源与用户权益。
SAEP 是面向 GUI Agent 操作第三方应用的声明协议。它将应用保护要求转换为机器可解析的策略,由系统在操作前完成 Agent 身份校验和策略决策,在关键能力路径实施约束,并记录可审计的决策与执行结果。
协议解决的问题:

| 逻辑组件 | 所属层面 | 核心职责 |
|---|---|---|
| Target App(三方应用) | 应用生态 | 声明应用、Activity 和 Intent 级操作边界;查询已注册 Agent 及本应用审计日志。 |
| Agent 策略管理 | 应用生态 | 以静态 Manifest 资源或动态更新方式表达限制规则,包含 default_policy 与 scope。 |
| Agent 身份查询 | 应用生态 | 对系统侧 Agent 身份信息进行查询和管理。 |
| GUI Agent | Agent 生态 | 完成任务规划、页面理解和意图识别;执行前提交目标、动作和上下文,请求系统决策。 |
| Agent 身份认证 | 系统控制面 | 管理 Agent 注册信息,校验包名、签名、UUID、可信等级和设备证明,并向声明应用提供查询。 |
| 策略管理模块 | 系统执行面 | 将决策落实到屏幕/虚拟显示、无障碍读取、点击、输入和注入等系统控制点。 |
| Agent 动作执行 | 系统执行面 | 承载屏幕与虚拟显示、无障碍与页面读取、点击、输入和注入等实际能力;只接受策略管理模块放行后的请求。 |
| TEE 服务 | 可信基础设施 | 保护可信身份和证明材料,关联 Agent 应用、实例身份与设备可信状态。 |
| 日志管理服务 | 审计基础设施 | 汇聚策略决策和 Agent 动作执行结果,补充系统可信字段,并按调用应用实施查询隔离。 |
| 安全存储 | 审计基础设施 | 保存策略、Agent 注册信息和审计记录,实施访问控制、完整性保护和生命周期管理。 |
系统基线是 SAEP 的最低边界,由系统统一维护,优先于应用声明和 Agent 自身判断。它用于防止三方应用未声明策略、策略填写错误或 Agent 试图绕过时出现能力意外放开。
BLOCK 或 CALL_USER 降级为 ALLOW。Agent 操作策略描述“某个 Agent 在当前上下文中能否对目标应用执行某项 GUI 操作”。最终结果至少由以下四类输入共同决定:
default_policy 与 scope。应用策略可在三个作用域表达:
| 作用域 | 匹配对象 | 示例 |
|---|---|---|
| App | 整个应用 | Agent 在本应用可在任意页面执行该操作。 |
| Activity | 指定页面 | 登录页禁止截图和输入,支付确认页完全禁止操作。 |
| Intent | 指定业务意图 | 限制内容修改、最终发布、删除或激励权益领取。 |
策略合并遵循三个稳定原则:系统基线不可放宽、多个命中结果取更严格值、解析失败回退到可用的默认策略。由于方案材料对 App、Activity、Intent 的简单顺序表述存在差异,开发者不应依赖“后写覆盖前写”;应让敏感页面显式声明限制,并通过目标镜像的验收用例确认最终 Effective Policy。
| 决策 | 系统行为 | 应用与 Agent 预期 |
|---|---|---|
ALLOW |
允许调用对应系统能力,并记录正常执行结果。 | 仍需继续通过应用业务鉴权;不代表免除用户授权。 |
BLOCK |
在 PEP 阻断操作,返回明确拒绝且不应回退到旁路能力。 | 停止自动操作,向用户说明限制,不尝试换通道绕过。 |
CALL_USER |
暂停自动化并展示系统级确认或接管界面。 | 等待用户亲自确认或操作,确认只覆盖所展示的具体步骤。 |
CLARIFY |
当前材料中用于表达澄清或软性约束。 | 具体语义和是否对外开放必须以目标 SDK/API 包为准。 |
强制执行应位于系统控制的关键路径。例如:应用级禁用可阻止目标应用进入 Agent 虚拟屏;页面禁止截图可增加安全图层;输入限制可在 InputManager 等路径阻断模拟注入。意图级约束是否已具备完整系统强制能力取决于目标版本,不能仅凭 Agent 返回“已遵守”完成验收。
策略决策和实际执行必须同时进入审计链路,才能识别“决策允许但执行失败”“决策拒绝但 Agent 尝试绕过”等差异。审计事件至少应关联 Agent 身份、会话与步骤、目标包和 Activity、操作意图、能力类型、策略版本、决策结果、执行结果及原因码。
| 项目 | 当前状态 |
|---|---|
| 支持系统版本 | ObricUI 2.2 版本及以上 |
三方应用可通过以下系统接口和配置入口接入 SAEP。调用前应先检测协议状态;接口不存在、调用异常或返回不可用时,应按 SAEP 不可用处理,并继续执行应用自身的控制。
| 所属类 | API 签名 | API 描述 |
|---|---|---|
android.security.obric.robots.RobotsHelperStub |
public static RobotsHelperStub getInstance() |
获取 SAEP 状态查询接口的单例对象。 |
android.security.obric.robots.RobotsHelperStub |
public boolean isRobotsEnabled(android.content.Context context) |
通过应用 Context 查询 SAEP 是否启用;true 表示启用。 |
android.security.obric.robots.RobotsHelperStub |
public boolean isRobotsEnabled() |
不传入 Context 查询 SAEP 是否启用;由系统实现侧获取上下文。 |
android.content.ContentResolver |
public int update(android.net.Uri uri, android.content.ContentValues values, java.lang.String where, java.lang.String[] selectionArgs) |
向 SAEP Policy Provider 提交动态策略;返回成功更新的记录数。 |
android.security.obric.agentmanager.AgentManagerStub |
public static AgentManagerStub getInstance() |
获取可信 Agent 查询接口的单例对象。 |
android.security.obric.agentmanager.AgentManagerStub |
public java.util.List<java.lang.String> listRegisteredAgents() |
查询设备中已注册 Agent 的 UUID 列表。 |
android.security.obric.agentmanager.AgentManagerStub |
public java.lang.String getAgentInfo(java.lang.String agentUuid) |
根据 Agent UUID 查询对应的注册身份信息,结果为 JSON 字符串。 |
android.security.obric.audit.SecurityAuditManagerStub |
public static SecurityAuditManagerStub getInstance() |
获取安全审计日志查询接口的单例对象。 |
android.security.obric.audit.SecurityAuditManagerStub |
public java.util.List<java.lang.String> queryLogs(long startTimeMs, long endTimeMs, java.lang.String targetIntent, java.lang.String targetActivity) |
按时间、操作意图和目标 Activity 查询与调用应用自身相关的审计日志。 |

应用应在进入依赖 SAEP 的功能前查询协议状态。返回 true 表示当前系统侧功能生效;返回 false 表示功能被云端关闭、系统版本不支持或暂不可用。检测失败时按“不支持”处理,并继续依赖应用自身安全控制。
// 判断 SAEP 协议功能当前是否生效(通过 Context 版本)
// 返回 true 表示生效,false 表示已被云端关闭或不可用
public static boolean isRobotsEnabled(Context context)
SAEP 状态查询(当前材料中的反射方式)
try {
Class<?> clazz =
Class.forName("android.security.obric.robots.RobotsHelperStub");
Method getInstance = clazz.getDeclaredMethod("getInstance");
getInstance.setAccessible(true);
Object instance = getInstance.invoke(null);
// 方式一:带 Context 参数版本
Method methodWithCtx = clazz.getDeclaredMethod("isRobotsEnabled",
Context.class);
methodWithCtx.setAccessible(true);
boolean enabled = (Boolean) methodWithCtx.invoke(instance, context);
Log.d(TAG, "SAEP protocol enabled: " + enabled);
} catch (Exception e) {
Log.e(TAG, "isRobotsEnabled failed: " + e.getMessage());
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
schema |
string | 是 | 固定格式:AGRP-Policy/1.0。Android GUI Robots 协议(AGRP,Android GUI Robots Policy)。 |
policy_version |
int | 是 | 策略版本号由应用自增;动态更新不得回滚。 |
package |
string | 否 | 设置管控规则的应用包名。 |
updated_at |
string | 是 | Unix 毫秒级时间戳字符串,用于记录 Policy 更新时间。 |
default_policy |
object | 是 | 解析失败时的回落策略,必须可用。 |
scope |
object | 否 | 应用级、Activity 页面级和 Agent 意图级细化规则。 |
顶层字段表只声明到 default_policy 和 scope。以下按当前策略示例及现有联调代码补齐 app、activities 两个维度的嵌套字段;未由正式 Schema 定义的必填、缺省与覆盖语义继续标为“未定义”。
app 维度字段
| 字段路径 | 类型 | 说明 |
|---|---|---|
default_policy.app |
object | 当前示例中的应用级默认回落策略容器;目前仅确认顶层 default_policy 必填。 |
default_policy.app.global_disable |
boolean | true 表示禁止全部 Agent 操作。 |
default_policy.app.screenshot_disable |
boolean | true 表示禁止截图或屏幕帧。 |
default_policy.app.input_disable |
boolean | true 表示禁止点击、文本输入等模拟输入。 |
scope.app |
object | 应用级细化策略容器;随非必填的 scope 整体省略。 |
scope.app.global_disable |
boolean | 当前策略示例已使用;语义同上。 |
scope.app.screenshot_disable |
boolean | 现有联调代码支持,主策略示例未展示;正式支持范围以 Schema 为准。 |
scope.app.input_disable |
boolean | 现有联调代码支持,主策略示例未展示;正式支持范围以 Schema 为准。 |
activities 维度字段
| 字段路径 | 类型 | 说明 |
|---|---|---|
scope.activities |
object / map | Activity 页面级规则集合;使用完整 Activity 类名作为动态键,不是数组。 |
scope.activities.<完整 Activity 类名> |
object | 单个页面的规则条目,例如 com.example.bank.LoginActivity。 |
scope.activities.<Activity>.name |
string | 面向用户展示的页面中文名称。 |
scope.activities.<Activity>.page_scope |
object | Activity 页面级限制项容器。 |
scope.activities.<Activity>.page_scope.global_disable |
boolean | true 表示禁止该页面全部 Agent 操作。 |
scope.activities.<Activity>.page_scope.screenshot_disable |
boolean | true 表示禁止该页面截图或屏幕帧。 |
scope.activities.<Activity>.page_scope.input_disable |
boolean | true 表示禁止该页面点击、文本输入等模拟输入。 |
agent_intents 维度字段
| 字段路径 | 类型 | 说明 |
|---|---|---|
scope.agent_intents |
object | Agent 意图级别;针对声明的意图场景,会在整个应用全局生效。 |
| 字段 | true 的含义 | 典型用途 |
|---|---|---|
global_disable |
禁止全部 Agent 操作 | 整个应用不允许 GUI Agent 操作。 |
screenshot_disable |
禁止截图/屏幕帧 | 登录、支付确认、隐私展示等敏感页面。 |
input_disable |
禁止点击、文本输入等模拟输入 | 密码、验证码、权限变更等页面。 |
modify_content |
限制修改内容或设置 | 昵称、通讯录、权限、设备与支付开关等。 |
post_content |
限制最终发送/发布 | 可辅助编辑,但不能代替用户完成最终发布。 |
delete_content |
限制最终删除 | 可帮助定位或选择对象,但不能执行删除。 |
account_incentive |
限制激励权益操作 | 签到、抽奖、秒杀、任务、积分或奖励领取。 |
统一语义:上述字段均以 true 表示“限制/禁止”,以 false 表示“不由该字段限制”,若未设定对应字段时将按照 false 进行处置。false 不代表系统最终一定允许,系统安全基线和其他更严格规则仍可拒绝操作,如支付场景。
global_disable=true 时,不再依赖截图或输入子项来放开能力。default_policy,并记录回退原因。文件:res/raw/agent_saep_policy.json
{
"schema": "AGRP-Policy/1.0",
"policy_version": 1,
"package": "com.example.bank",
"updated_at": "1772191606000",
"default_policy": {
"app": {
"global_disable": true,
"screenshot_disable": false,
"input_disable": true
}
},
"scope": {
"app": {
"global_disable": false
},
"activities": {
"com.example.bank.LoginActivity": {
"name": "登录界面",
"page_scope": {
"global_disable": true,
"screenshot_disable": true,
"input_disable": true
}
},
"com.example.bank.PayConfirmActivity": {
"name": "支付确认界面",
"page_scope": {
"screenshot_disable": true
}
}
},
"agent_intents": {
"modify_content": false,
"delete_content": false,
"account_incentive": true,
"post_content": false
}
}
}
静态声明是当前材料推荐的基础接入方式。系统在应用安装或更新时读取并缓存策略,不依赖应用进程已启动。
文件:AndroidManifest.xml
<application
... >
<meta-data
android:name="com.obric.agentrobots.POLICY_JSON"
android:resource="@raw/agent_saep_policy" />
...
</application>
本节按当前接口材料使用 @raw/agent_saep_policy.json。开发者可根据自身代码规范对资源名称进行自定义命名。
当策略需要随用户状态、云控或业务条件变化时,可通过系统 Provider 更新。当前初稿示例 URI 为:
content://com.obric.agentrobots.provider/policy
请求参数:
policy:完整策略 JSON 字符串。version:整数版本号,必须高于当前已生效版本。动态策略更新
private static final Uri POLICY_UPDATE_URI =
Uri.parse("content://com.obric.agentrobots.provider/policy");
/**
* 通过 ContentProvider 更新策略
*
* @param context 上下文
* @param policy 策略 JSON 字符串
* @param version 版本号
* @return 更新影响的行数,负数表示失败
*/
public static int updatePolicy(Context context, String policy, int version) {
ContentValues values = new ContentValues();
values.put("policy", policy);
values.put("version", version);
return context.getContentResolver().update(
POLICY_UPDATE_URI, values, null, null);
}
应用必须在本地完成 JSON Schema 校验后再提交。更新失败时保留上一有效版本,不得先清空旧策略;失败结果要进入应用自身监控。
| 错误码 | 处理建议 |
|---|---|
E_ACCESS_DENIED |
检查调用包名、签名和权限。 |
E_INVALID_ARGUMENT |
检查参数、JSON 与版本类型。 |
E_POLICY_PARSE_ERROR / E_POLICY_VALIDATION |
检查字段名、Schema、枚举值和 JSON 合法性;注意原材料中的 POLICTY 拼写可能是实现兼容项。 |
E_POLICY_VERSION_ROLLBACK |
生成更高版本号后重试,禁止复用旧版本。 |
E_POLICY_NOT_MORE_RESTRICTIVE |
策略包含无法收敛的放宽项;保持原策略并按镜像规则调整。 |
E_SIGNATURE_INVALID |
检查包签名与登记信息。 |
E_RATE_LIMITED |
建议合并更新,避免频繁调用。 |
E_STORAGE_UNAVAILABLE / E_INTERNAL_ERROR |
保留上一有效策略,记录告警并稍后重试。 |
开发者可以根据自身应用工程情况选择静态策略声明或动态更新,两种方案保证至少有一个接入即可。推荐开发者使用静态策略声明进行更新,系统会在应用包安装和更新时自动捕获最新的策略文件。
系统提供端侧已注册 Agent 列表和指定 Agent 信息查询。
package android.security.obric.agentmanager;
public class RobotsHelperStub {
/**
* 获取指定Agent的信息
*
* @param agentUuid Agent的 UUID
* @return Agent信息的 JSON 字符串
*/
public String getAgentInfo(String agentUuid) {
}
/**
* 获取已注册的Agent列表
*
* @return 已注册Agent的 UUID 列表
*/
public List<String> listRegisteredAgents() {
}
}
系统提供在端侧已经注册的可信 Agent 进行查询。查询结果可能包含 Agent 名称、UUID、应用包名、签名摘要、Agent 类型、可信等级和版本等信息。应用应将该信息用于展示和审计关联,不应自行把“已注册”推导为对任意业务动作已获授权。

以下代码保留原文的异常处理和反射调用方式。正式 SDK 可用后应优先改用稳定的编译期接口。
1. 获取已注册的 UUID
try {
Class<?> stubClazz =
Class.forName("android.security.obric.agentmanager.AgentManagerStub");
Method getInstanceMethod = stubClazz.getDeclaredMethod("getInstance");
getInstanceMethod.setAccessible(true);
Object stub = getInstanceMethod.invoke(null);
Method listMethod = stubClazz.getDeclaredMethod("listRegisteredAgents");
listMethod.setAccessible(true);
List<String> uuids = (List<String>) listMethod.invoke(stub);
// uuids 即已注册的AgentUUID 列表
} catch (Exception e) {
e.printStackTrace();
}
2. 通过 UUID 获取对应 Agent 应用信息
try {
Class<?> stubClazz =
Class.forName("android.security.obric.agentmanager.AgentManagerStub");
Method getInstanceMethod = stubClazz.getDeclaredMethod("getInstance");
getInstanceMethod.setAccessible(true);
Object stub = getInstanceMethod.invoke(null);
Method getInfoMethod = stubClazz.getDeclaredMethod("getAgentInfo",
String.class);
getInfoMethod.setAccessible(true);
String uuid = "<YOUR_AGENT_UUID>";
String info = (String) getInfoMethod.invoke(stub, uuid);
// info 即该Agent的详情信息(JSON 字符串)
} catch (Exception e) {
e.printStackTrace();
}
系统与 Agent 会统一记录所有 Agent 的操作日志。作为被操作方,你的应用也将被赋予查询相关日志的权限。应用只能查询与自身包名相关的记录。建议在后台线程异步查询,按时间窗口分页或分批处理,避免阻塞主线程。
系统将提供一个标准接口,允许你查询与你的应用相关的 Agent 操作记录。支持查询范围如下:

package android.security.obric.audit;
public class SecurityAuditManagerStub {
/**
* 查询调用方自己的审计日志。
*
* @param startTimeMs 日志开始时间戳(毫秒);0 表示不限制
* @param endTimeMs 日志结束时间戳(毫秒);0 表示不限制
* @param targetIntent 查询意图;为空则不按意图过滤
* @param targetActivity 查询目标 Activity;为空则不按 Activity 过滤
* @return 原始 JSON 日志字符串列表;失败返回空列表
*/
public List<String> queryLogs(long startTimeMs, long endTimeMs,
String targetIntent, String targetActivity) {
}
}
| 分类 | 字段名 | 类型 | 是否可空 | 说明 |
|---|---|---|---|---|
agent_identify(Agent 身份) |
agent_name |
String | 否 | Agent 名称。 |
agent_identify(Agent 身份) |
agent_uuid |
String | 否 | Agent 实例的唯一标识,来自其身份声明。 |
agent_identify(Agent 身份) |
pkg_sha256 |
String | 否 | Agent 应用签名的 SHA256。 |
agent_identify(Agent 身份) |
agent_type |
Enum | 否 | Agent 类型(如 ASSISTIVE_EXECUTION、AUTONOMOUS)。 |
agent_identify(Agent 身份) |
trust_level |
Enum | 否 | Agent 的可信等级(如 TRUSTED_TEE、UNTRUSTED),由系统层 TEE 决定并填充。 |
agent_identify(Agent 身份) |
package_name |
String | 否 | 发起调用的进程 UID 及其包名,由系统填充。 |
agent_identify(Agent 身份) |
agent_version |
String | 是 | Agent app 的版本号。 |
agent_identify(Agent 身份) |
agent_md5 |
String | 是 | Agent app 的 apk 文件哈希。 |
agent_identify(Agent 身份) |
proof_signature |
String | 否 | 由 TEE 产出,针对 Agent 注册的可信身份证明签名。 |
agent_decision(操作记录与决策记录) |
log_source |
String | 否 | 日志来源:agent:Agent 决策日志;framework:InputManagerService 和 WindowsManager 插桩日志。 |
agent_decision(操作记录与决策记录) |
timestamp |
String | 否 | 日志的时间戳。 |
agent_decision(操作记录与决策记录) |
event_id |
String | 否 | 单次事件的全局唯一标识符。 |
agent_decision(操作记录与决策记录) |
session_id |
String | 否 | Agent 的会话 ID,用于关联一个任务流程中的所有事件。 |
agent_decision(操作记录与决策记录) |
step_id |
String | 否 | Agent 操作的步骤 id。 |
agent_decision(操作记录与决策记录) |
target_package |
String | 否 | 操作目标应用的包名。 |
agent_decision(操作记录与决策记录) |
target_activity |
String | 是 | 操作目标应用的 Activity 名称。 |
agent_decision(操作记录与决策记录) |
intent_type |
String | 是 | Agent 的操作意图分类(如 ACCOUNT_AUTH、ASSET_TRANSACTION)。若未进行意图判断,该字段为空。 |
agent_decision(操作记录与决策记录) |
decision_outcome |
Enum | 否 | 云侧返回决策结果:BLOCK、CALL_USER、CLARIFY、ALLOW。 |
agent_decision(操作记录与决策记录) |
user_authorization_type |
Enum | 是 | 用户授权类型:CALL_USER、EXPLICIT_AGREEMENT、AUTO_EXECUTION。 |
agent_decision(操作记录与决策记录) |
action_type |
String | 否 | 本次操作使用的核心能力(如 SCREENSHOT、CLICK)。 |
agent_decision(操作记录与决策记录) |
action_args |
List | 否 | 本次操作的参数;截图场景传入 activity。 |
agent_decision(操作记录与决策记录) |
policy |
String | 是 | 原始策略管理 json 原文。若目标应用不存在管控策略,此处为空。 |
agent_decision(操作记录与决策记录) |
reason_code |
String | 是 | Agent 决策原因,如 APP_POLICY_GLOBAL、SYSTEM_BASELINE、APP_POLICY_ACTIVITY、APP_POLICY_INTENT、NULL。策略更新:如 MANIFEST、CONTENT_PROVIDER。 |
agent_decision(操作记录与决策记录) |
duration_ms |
Long | 是 | 操作执行耗时(毫秒)。操作被拒绝/用户接管场景下,默认值为 0。 |
List<String> logs = new ArrayList<>();
try {
Class<?> clazz =
Class.forName("android.security.obric.audit.SecurityAuditManagerStub");
Method method = clazz.getDeclaredMethod("queryLogs",
long.class, long.class, String.class, String.class);
method.setAccessible(true);
Object ret = method.invoke(null, startMs, endMs, intentParam,
activityParam);
if (ret instanceof List) {
logs.addAll((List<String>) ret);
//TO DO: 实现业务逻辑
}
} catch (Exception e) {
Log.e(TAG, "queryLogs error: " + e.getMessage());
}
日志存储在端侧安全区域,日志存储规则满足以下任意一个条件将会进行滚动删除。
schema 与 policy_version。应用声明只表达应用侧边界,不能覆盖系统安全基线、Agent 身份校验、用户授权或业务风控。请结合日志中的 decision_outcome 与 reason_code 定位。
为便于开发者理解并验证 SAEP 接入流程,我们提供开源示例工程 SAEP Demo,包含以下功能:
| 平台 | 仓库地址 |
|---|---|
| GitHub | bytedance/SAEP-demo |
| Gitee | ByteDance/SAEP-demo |