从源码层面分析 AppFunction 框架的设计意图、核心组件和调用链路。读完本文,你应该能回答三个问题:AppFunction 解决什么问题?一个完整调用在系统层面经历了哪些步骤?如何为自己的 App 接入 AppFunction?
一、AppFunction 解决什么问题
Android 的跨 App 通信方式发展到现在,经历过几代方案。Intent 解决了"启动另一个 App"的问题,ContentProvider 解决了"跨 App 共享数据"的问题,但它们有一个共同的局限:调用方无法像调用本地函数那样,给目标 App 传递结构化参数并拿到结构化返回值。
AppFunction 填补的正是这个缺口。 它在 Android 16(API 36)中引入,让 App A 能以函数调用的方式请求 App B 执行一个操作,传入参数,接收返回值。整个调用链路------函数发现、权限校验、Service 绑定、参数传递、取消机制------全部由 system_server 接管,开发者不需要手动处理 Binder 通信。
这本质上是一个系统级 RPC(Remote Procedure Call)框架,运行在 Android Framework 层。AppFunctionManager 的角色不是执行者,而是路由器------它知道哪个 App 注册了哪些函数,并负责安全地建立连接。
用调用方、system_server、提供方三者的交互来理解:
javascript
调用方 App A system_server 提供方 App B
(比如闹钟 App) (AppFunctionManagerService) (比如日历 App)
"帮我查下周三 收到请求: CalendarProvider
的日程是否冲突" ① 查元数据 → 找到日历 App
→ executeAppFunction ② 权限校验 → 通过
("com.calendar", ③ 绑定 Service
"checkConflict", ④ 调用 onExecuteFunction()
{date: "周三"}) ⑤ 接收返回值 → 返回调用方 → 处理并返回 {conflict: false}
← 收到结果 {conflict: false}
调用方只知道目标 App 的包名和函数名,不需要知道目标 App 的进程状态、Service 是否已启动、数据传输协议是什么------这些全部交给 system_server 处理。
二、源码跟踪:一个完整的 add 调用
与其抽象地描述架构,不如直接跟踪一段真实代码。下面引用 Android CTS 测试套件中的代码,CTS(Compatibility Test Suite)是 Google 用来验证 Android 兼容性的测试框架,每个 AppFunction API 都经过它测试,可以保证准确性。
场景:一个提供方 App(CTS 测试包)暴露了 add(a, b) 函数,调用方传 a=1, b=2,返回 3。
2.1 时序图
scss
调用方 system_server 提供方 Service
(测试用例) (AppFunctionManagerService) (TestAppFunctionService)
│ ① 构建请求 │ │
│ Request.Builder │ │
│ ("cts", "add") │ │
│ .setParams({a:1, b:2}) │ │
│ │ │
│ ② manager.executeAppFunction()│ │
│───────────────────────────────>│ │
│ │ │
│ │ ③ Binder 进入 system_server │
│ │ 验证 callingPackage │
│ │ 检查企业策略 │
│ │ 查 AppSearch 元数据 │
│ │ → "add" 已注册, enabled=true │
│ │ 权限校验 │
│ │ → restrictExecFunc=true, │
│ │ 调用方持有权限 → pass │
│ │ resolveAppFunctionService │
│ │ │
│ │ ④ bindService(BIND_AUTO_CREATE) │
│ │───────────────────────────────────>│
│ │ │ onCreate()
│ │ │
│ │ ⑤ 调用 onExecuteFunction() │
│ │ 传入: request, callingPackage, │
│ │ signingInfo, │
│ │ cancellationSignal, │
│ │ callback │
│ │───────────────────────────────────>│
│ │ │
│ │ │ ⑥ 路由 + 计算
│ │ │ switch(funcId):
│ │ │ case "add":
│ │ │ a = params.getLong("a") → 1
│ │ │ b = params.getLong("b") → 2
│ │ │ sum = 1+2 = 3
│ │ │
│ │ ⑦ callback.onResult({ │
│ │ returnValue: 3 }) │
│ │<───────────────────────────────────│
│ │ │
│ │ ⑧ 记录日志 → 返回结果给调用方 │
│ │ │
│ ⑨ OutcomeReceiver.onResult() │ │
│ 收到 resultDoc │ │
│ .getLong("returnValue") │ │
│ → 3 ✅ │ │
│<───────────────────────────────│ │
│ │ ⑩ unbindService │
│ │───────────────────────────────────>│ onDestroy()
2.2 逐步骤源码
下面每一段代码都可以在 AOSP 中找到原文,路径已标注。
步骤 ①-②:调用方代码
java
// 文件: cts/tests/appfunctions/src/android/app/appfunctions/cts/AppFunctionManagerTest.kt
// 简化自: executeAppFunction_platformManager_platformAppFunctionService_success_nonParam()
// 1. 构建参数
GenericDocument params = new GenericDocument.Builder<>("", "", "")
.setPropertyLong("a", 1)
.setPropertyLong("b", 2)
.build();
// 2. 构建请求
ExecuteAppFunctionRequest request = new ExecuteAppFunctionRequest.Builder(
"android.app.appfunctions.cts", // 目标包名
"add" // 函数名
).setParameters(params).build();
// 3. 获取系统服务
AppFunctionManager manager = context.getSystemService(AppFunctionManager.class);
// 4. 执行异步调用
manager.executeAppFunction(
request,
context.getMainExecutor(),
new CancellationSignal(),
new OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>() {
@Override
public void onResult(ExecuteAppFunctionResponse response) {
long result = response.getResultDocument()
.getPropertyLong(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE);
// result == 3
}
@Override
public void onError(AppFunctionException error) {
Log.e(TAG, "Failed: " + error.getErrorMessage());
}
}
);
调用方的代码很简洁:构建参数 → 构建请求 → 获取系统服务 → 异步执行。所有复杂逻辑都在 system_server 内部。
步骤 ③:system_server 核心路由逻辑
scss
// 文件: frameworks/base/services/appfunctions/java/com/android/server/appfunctions/
// AppFunctionManagerServiceImpl.java
// 简化自 executeAppFunctionInternal() 方法
@WorkerThread
private void executeAppFunctionInternal(ExecuteAppFunctionAidlRequest request, ...) {
// 3.1 企业策略检查
if (!mCallerValidator.verifyEnterprisePolicyIsAllowed(callingUser, targetUser)) {
callback.onError(ERROR_ENTERPRISE_POLICY_DISALLOWED);
return;
}
// 3.2 调用者权限检查(查 AppSearch 元数据 + 执行权限验证)
mCallerValidator
.verifyCallerCanExecuteAppFunction(callingUid, ..., functionIdentifier)
.thenCompose(canExecuteResult -> {
if (canExecuteResult == DENIED) {
return failedFuture(new SecurityException("no permission"));
}
// 3.3 检查函数是否被用户禁用
return isAppFunctionEnabled(functionIdentifier, targetPackage, ...);
})
.thenAccept(canExecuteResult -> {
// 3.4 解析目标 Service 的 Intent
Intent serviceIntent = mInternalServiceHelper
.resolveAppFunctionService(targetPackageName, targetUser);
// 3.5 绑定 Service
bindAppFunctionServiceUnchecked(request, serviceIntent, ...);
});
}
system_server 在 Binder 线程上执行这五步检查,全部通过后才会连接目标 Service。任何一步失败都会直接回调 onError。
步骤 ⑥-⑦:提供方 Service 实现
less
// 文件: cts/tests/appfunctions/testutils/src/android/app/appfunctions/testutils/
// TestAppFunctionService.java
public class TestAppFunctionService extends AppFunctionService {
@Override
@MainThread // 系统保证在主线程回调
public void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull SigningInfo callingPackageSigningInfo,
@NonNull CancellationSignal cancellationSignal,
@NonNull OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback) {
// 1. 注册取消监听
cancellationSignal.setOnCancelListener(() -> cancelOperation());
// 2. 按函数名路由
switch (request.getFunctionIdentifier()) {
case "add" -> {
long a = request.getParameters().getPropertyLong("a"); // 1
long b = request.getParameters().getPropertyLong("b"); // 2
long sum = a + b; // 3
// 3. 构建返回值,使用 PROPERTY_RETURN_VALUE 作为 key
GenericDocument result = new GenericDocument.Builder<>("", "", "")
.setPropertyLong(
ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, sum)
.build();
// 4. 回传结果
callback.onResult(new ExecuteAppFunctionResponse(result));
}
// ... 其他 case ...
}
}
}
三、函数元数据:AppFunctionManager 怎么知道有哪些函数
AppFunctionManager 不是静态配置的,它的函数发现依靠 Android 的 AppSearch 索引引擎。整个流程在 App 安装时自动触发。
第一步:App 在 assets/appfunctions.xml 中声明函数定义。
xml
<!-- 文件: cts/tests/appfunctions/assets/appfunctions.xml -->
<appfunctions>
<appfunction>
<function_id>add</function_id> <!-- 函数唯一标识 -->
<enabled_by_default>true</enabled_by_default> <!-- 默认启用 -->
<schema_category>utils</schema_category> <!-- 分类标签 -->
<restrict_callers_with_execute_app_functions>true <!-- 需要执行权限 -->
</restrict_callers_with_execute_app_functions>
</appfunction>
<appfunction>
<function_id>add_disabledByDefault</function_id>
<enabled_by_default>false</enabled_by_default> <!-- 默认禁用 -->
</appfunction>
</appfunctions>
第二步:在 AndroidManifest.xml 中关联 Service 和元数据。
xml
<!-- 文件: cts/tests/appfunctions/AndroidManifest.xml -->
<service android:name=".TestAppFunctionService"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
android:exported="true"
android:process=":appfunctions">
<property
android:name="android.app.appfunctions"
android:value="appfunctions.xml"/>
<intent-filter>
<action android:name="android.app.appfunctions.AppFunctionService"/>
</intent-filter>
</service>
第三步:安装时自动索引。 App 安装后,appsindexer 解析 XML,将函数信息写入 AppSearch 的 AppFunctionStaticMetadata 数据库。这是一个 GenericDocument 类型的结构化索引,AppFunctionManagerService 在收到调用请求时从这里检索目标函数的元数据。
AppSearch 中实际存储了两层元数据:
| 层次 | 命名空间 | 内容 | 写入者 | 读取者 |
|---|---|---|---|---|
| 静态元数据 | AppFunctionStaticMetadata |
XML 声明的函数信息(functionId、enabledByDefault、schemaCategory) | appsindexer(安装时) | AppFunctionManagerService |
| 运行时元数据 | AppFunctionRuntimeMetadata |
用户控制的状态(启用/禁用) | AppFunctionManager(setAppFunctionEnabled) | AppFunctionManagerService |
两者通过 JoinSpec 关联查询,最终判断一个函数是否可以被调用。这体现了 Android 设计中的一个思路------静态声明 + 运行时覆盖,注册和授权解耦。
四、核心组件全景图
把前面分散的信息汇总成一张图:
css
┌───────────────────────────────────────────────────────────────────┐
│ 调用方 App A │
│ AppFunctionManager │
│ .executeAppFunction(request, executor, cancelSig, callback) │
│ .isAppFunctionEnabled(functionId) ← 查询启用状态 │
│ .setAppFunctionEnabled(functionId, state) ← 用户开关 │
│ .getAppFunctionRuntimeMetadata(...) ← 发现可用函数 │
└───────────────────────────┬───────────────────────────────────────┘
│ Binder IPC
▼
┌───────────────────────────────────────────────────────────────────┐
│ system_server (特权进程) │
│ │
│ AppFunctionManagerService ← Binder Stub, 对外接口 │
│ │ │
│ AppFunctionManagerServiceImpl ← 核心实现 │
│ │ │
│ ├─ CallerValidator → 权限校验(3 层) │
│ ├─ ServiceHelper → 解析目标 Service Intent │
│ ├─ RemoteServiceCaller → bindService + IPC 封装 │
│ ├─ AppFunctionManagerHelper → AppSearch 元数据查询 │
│ └─ AppFunctionsLoggerWrapper → StatsLog 埋点 │
│ │
│ 检查流程(顺序执行): │
│ 1. 校验调用方包名 │
│ 2. 验证用户句柄 │
│ 3. 企业策略检查 │
│ 4. AppSearch 查元数据 → 验证函数存在 + 权限 + 启用状态 │
│ 5. 解析目标 Service Intent │
│ 6. bindService + IPC 调用 │
└───────────────────────────┬───────────────────────────────────────┘
│ Binder IPC
▼
┌───────────────────────────────────────────────────────────────────┐
│ 提供方 App B │
│ │
│ AppFunctionService │
│ .onExecuteFunction(request, callingPkg, signingInfo, cancel, │
│ callback) │
│ │
│ 数据流: │
│ 入参: GenericDocument { "a": 1, "b": 2 } │
│ 返回: ExecuteAppFunctionResponse( │
│ GenericDocument { "returnValue": 3 }) │
└───────────────────────────────────────────────────────────────────┘
权限模型
AppFunction 的权限分为两层,分别保护提供方和调用方:
| 权限 | 声明位置 | 保护级别 | 作用 |
|-----------------------------|---------------------------|-----------|--------------------------------|-------------------------------------|
| BIND_APP_FUNCTION_SERVICE | 提供方 Manifest 的 service 标签 | signature | 只有系统能绑定你的 Service,第三方 App 无法连接 |
| EXECUTE_APP_FUNCTIONS | 调用方 Manifest | internal | privileged | 允许调用其他 App 的函数;没有此权限只能调用自己 App 内的函数 |
两层权限的设计意图很明确:提供方只信任系统,调用方需显式声明意图。两者叠加后,攻击者无法伪造调用链,也无法绕过系统直接连接提供方。
五、调用链路总结
将前面分析的所有步骤压缩成一张时序图:
scss
App A system_server App B
│ │ │
│ manager.executeAppFunction() │ │
│─────────────────────────────>│ │
│ │ │
│ ┌────────┴──────────┐ │
│ │ ① 校验包名 │ │
│ │ ② 企业策略 │ │
│ │ ③ 查 AppSearch │ │
│ │ 元数据 + 权限 │ │
│ │ ④ 检查启用状态 │ │
│ │ ⑤ 解析 Service │ │
│ └────────┬──────────┘ │
│ │ │
│ │ bindService() + │
│ │ onExecuteFunction() │
│ │────────────────────────────────>│
│ │ │
│ │ switch(funcId) │
│ │ parse params │
│ │ execute logic │
│ │ callback.onResult│
│ │ │
│ │ callback.onResult() │
│ │<────────────────────────────────│
│ │ │
│ │ unbindService() │
│ │────────────────────────────────>│
│ │ │ onDestroy()
│ OutcomeReceiver.onResult() │ │
│<─────────────────────────────│ │
调用方全程不知道自己经历了 Service 的冷启动、Binder 通信、异步回调线程切换------它只看到请求发出去了,结果回来了。
六、开发最小步骤:为自己的 App 接入 AppFunction
第 1 步:创建 AppFunctionService
scala
public class MyAppFunctionService extends AppFunctionService {
@Override
public void onExecuteFunction(
ExecuteAppFunctionRequest request,
String callingPackage,
SigningInfo signingInfo,
CancellationSignal cancelSignal,
OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback) {
// onExecuteFunction 在主线程回调,耗时操作需切线程
mExecutor.execute(() -> {
try {
switch (request.getFunctionIdentifier()) {
case "createNote":
String title = request.getParameters().getPropertyString("title");
String content = request.getParameters().getPropertyString("content");
String noteId = myNoteRepo.create(title, content);
GenericDocument result = new GenericDocument.Builder<>("", "", "")
.setPropertyString("returnValue", noteId)
.build();
callback.onResult(new ExecuteAppFunctionResponse(result));
break;
default:
callback.onError(new AppFunctionException(
AppFunctionException.ERROR_FUNCTION_NOT_FOUND,
"Unknown function"));
}
} catch (Exception e) {
callback.onError(new AppFunctionException(
AppFunctionException.ERROR_APP_UNKNOWN_ERROR, e.getMessage()));
}
});
}
}
第 2 步:配置 Manifest
ini
<service android:name=".MyAppFunctionService"
android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
android:exported="true"
android:process=":functions">
<property
android:name="android.app.appfunctions"
android:value="appfunctions.xml"/>
<intent-filter>
<action android:name="android.app.appfunctions.AppFunctionService"/>
</intent-filter>
</service>
第 3 步:声明函数元数据
assets/appfunctions.xml:
xml
<appfunctions>
<appfunction>
<function_id>createNote</function_id>
<enabled_by_default>true</enabled_by_default>
<restrict_callers_with_execute_app_functions>true</restrict_callers_with_execute_app_functions>
</appfunction>
</appfunctions>
第 4 步:调用方调用
java
AppFunctionManager manager = context.getSystemService(AppFunctionManager.class);
GenericDocument params = new GenericDocument.Builder<>("", "", "")
.setPropertyString("title", "shopping list")
.setPropertyString("content", "milk, eggs")
.build();
ExecuteAppFunctionRequest request = new ExecuteAppFunctionRequest.Builder(
"com.example.notesapp",
"createNote"
).setParameters(params).build();
manager.executeAppFunction(request, executor, new CancellationSignal(),
new OutcomeReceiver<>() {
@Override
public void onResult(ExecuteAppFunctionResponse response) {
String noteId = response.getResultDocument()
.getPropertyString("returnValue");
Log.i(TAG, "Note created: " + noteId);
}
@Override
public void onError(AppFunctionException error) {
Log.e(TAG, "Failed: " + error.getErrorMessage());
}
});
七、开发注意事项
| 要点 | 说明 |
|---|---|
| 独立进程 | Service 推荐 android:process=":functions",与主 App UI 进程隔离 |
| 线程切换 | onExecuteFunction 在主线程回调,耗时操作必须切换到后台线程 |
| 异常保护 | 所有代码包裹 try-catch,crash 不要泄漏到 system_server |
| 取消支持 | 监听 CancellationSignal,支持调用方主动取消 |
| 回调保证 | 必须且仅一次调用 callback.onResult 或 callback.onError,否则调用方永久等待 |
| 安全层次 | Signature 权限 → 调用方权限 → 元数据 restrict → 用户运行时开关 → SigningInfo 透传 |
八、当前状态
| 组件 | 状态 |
|---|---|
| AOSP 框架基础设施 | 就绪(Android 16) |
| CTS 测试覆盖 | 完整(平台 API + Sidecar 库 + 异常路径) |
| Sidecar 扩展库 | 就绪(com.android.extensions.appfunctions) |
| 预定义 Schema | 独立 SDK(Google Maven),不在 AOSP 中 |
| 参考 App | 目前仅 CTS 测试提供完整示例 |
本文基于 AOSP frameworks/base 和 cts/tests/appfunctions 源码编写,所有代码片段均可追溯到具体文件路径。