AppOps 与权限
本文承接 权限检查、权限类型 和 权限服务架构。checkPermission 只回答 permission flags 是否授予;相机、位置、麦克风、通知等真实访问还可能由 AppOps 再限制。本文沿 Android 17 PermissionCheckerService 和 PermissionService 追踪 permission-to-op 映射、self/proxy attribution、noteOp/startOp/finishOp 以及 MODE_ALLOWED/IGNORED/ERRORED/DEFAULT 的消费者边界。
1. 两层状态
1.1 Permission flags
PermissionService 从 AccessState 读取 appId、userId、deviceId 下的 permission flags。flags 表明是否有 RUNTIME_GRANTED、PROTECTION_GRANTED、用户或策略固定,但不记录一次 API 调用是否被 AppOps 允许。
1.2 AppOps mode
AppOps 以 (op, uid, package, attributionTag) 为键记录操作模式。它可以在 permission 仍 granted 时返回 ignored/errored,也可以在 permission 默认状态下依据 permission-to-op 规则继续检查 permission。
2. permission-to-op
2.1 映射入口
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
final int op = AppOpsManager.permissionToOpCode(permission);
if (op < 0) {
Slog.wtf(LOG_TAG,
"Appop permission " + permission
+ " with no app op defined!");
return PermissionChecker.PERMISSION_HARD_DENIED;
}只有带 AppOp 语义的 permission 才能进入该分支。平台 runtime permission 若声明为 appop 却找不到 op 映射,会被视为系统不变量破坏并 hard denied,而不是默认 allowed。
2.2 无映射权限
runtime permission 没有 op 时仍可由 PermissionService 判断 flags;例如某些后台修饰权限没有独立 op。PermissionChecker 会记录这种特殊情况并继续 permission chain,不能把 OP_NONE 解释为“忽略所有权限检查”。
3. AppOp 检查
3.1 AppOp permission
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private static int checkAppOpPermission(
Context context,
PermissionManagerServiceInternal permissionManager,
String permission,
AttributionSource attributionSource,
String message,
boolean forDataDelivery,
boolean fromDatasource) {
final int op = AppOpsManager.permissionToOpCode(permission);
if (op < 0) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
final int opMode = performOpTransaction(
context, attributionSource.getToken(), op,
attributionSource, message, forDataDelivery,
false, false, true, false,
AppOpsManager.OP_NONE,
AppOpsManager.ATTRIBUTION_FLAGS_NONE,
AppOpsManager.ATTRIBUTION_FLAGS_NONE,
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
if (opMode == AppOpsManager.MODE_IGNORED
|| opMode == AppOpsManager.MODE_ERRORED) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
return PermissionChecker.PERMISSION_GRANTED;
}AppOp path先执行操作事务,再依据 mode 返回 hard denied 或 granted。MODE_IGNORED 在 PermissionChecker 这一层可能被转换为 hard denied;具体资源 API 是否返回空结果,还取决于调用它的服务如何解释 PermissionChecker 结果。
3.2 Default mode
case AppOpsManager.MODE_DEFAULT: {
if (!skipCurrentChecks && !checkPermission(
context, permissionManager, permission,
attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (next != null && !checkPermission(
context, permissionManager, permission, next)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
break;
}MODE_DEFAULT 不等于允许,它要求继续做 permission 检查。只有 permission 层通过,AppOps default 才能最终返回 granted。
4. Runtime 与 op
4.1 双层顺序
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private static int checkRuntimePermission(
Context context,
PermissionManagerServiceInternal permissionManager,
String permission,
AttributionSource attributionSource,
String message,
boolean forDataDelivery,
boolean startDataDelivery,
boolean fromDatasource,
int attributedOp) {
final int op = AppOpsManager.permissionToOpCode(permission);
if (!checkPermission(context, permissionManager,
permission, attributionSource)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
if (op < 0) {
return PermissionChecker.PERMISSION_GRANTED;
}
final int opMode = performOpTransaction(
context, attributionSource.getToken(), op,
attributionSource, message, forDataDelivery,
startDataDelivery, false, true, false,
attributedOp,
AppOpsManager.ATTRIBUTION_FLAGS_NONE,
AppOpsManager.ATTRIBUTION_FLAGS_NONE,
getAttributionChainId(startDataDelivery,
attributionSource));
if (opMode == AppOpsManager.MODE_ERRORED) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}
return opMode == AppOpsManager.MODE_IGNORED
? PermissionChecker.PERMISSION_SOFT_DENIED
: PermissionChecker.PERMISSION_GRANTED;
}runtime path先检查 permission grant,再检查 op。MODE_IGNORED 产生 soft denied,允许资源服务返回空/降级结果;MODE_ERRORED 产生 hard denied。没有 op 的 runtime permission 在 permission 通过后直接 granted。
4.2 AppOps 写入
安装参数或权限策略改变 appop permission 时,PermissionService 通过 AppIdAppOpPolicy 写入 mode;runtime grant 本身不等于把所有 op 强制设为 allowed。用户在设置中改变 AppOps,可能不改变 PermissionFlags,却立即改变真实 API 结果。
5. Attribution 链
5.1 self 与 proxy
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
final boolean selfAccess = singleReceiverFromDatasource
|| next == null;
final AttributionSource accessorSource =
singleReceiverFromDatasource ? next : current;
if (selfAccess) {
notedOpResult = appOpsManager.noteOpNoThrow(
notedOp, resolvedAttributionSource, message);
} else {
notedOpResult = appOpsManager.noteProxyOpNoThrow(
notedOp, resolvedAttributionSource,
message, skipProxyOperation);
}单节点链使用 self op,多节点链使用 proxy op。source/datasource 和 accessor/receiver 的角色不同,AppOps 必须分别记录,否则数据提供者会把代理者的访问归因到错误 UID。
5.2 信任链
if (!isDatasource && next != null
&& !current.isTrusted(context)) {
return PermissionChecker.PERMISSION_HARD_DENIED;
}存在后继 attribution source 时,当前节点必须可信;未注册或无权声明 UPDATE_APP_OPS_STATS 的链会在数据访问前失败。PermissionManagerService 的 AttributionSourceRegistry 与 PermissionChecker 共同维护这一不变量。
6. 数据访问生命周期
6.1 失败回收
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
final int result = checkPermission(
mContext, mPermissionManagerServiceInternal,
permission, attributionSource, message,
forDataDelivery, startDataDelivery,
fromDatasource, attributedOp);
if (startDataDelivery
&& result != PermissionChecker.PERMISSION_GRANTED
&& result != PermissionChecker.PERMISSION_SOFT_DENIED) {
finishDataDelivery(
attributedOp == AppOpsManager.OP_NONE
? AppOpsManager.permissionToOpCode(permission)
: attributedOp,
attributionSource.asState(), fromDatasource);
}如果 start data delivery 后得到 hard denied,服务必须 finish 已开始的 op;soft denied 不触发同样的回收分支,因为资源服务可能仍在执行可降级访问。遗漏 finish 会让 AppOps 留下 running operation。
6.2 finish 链
appOpsManager.finishOp(attributionSourceState.token,
op, resolvedAccessorSource);
appOpsManager.finishProxyOp(attributionSourceState.token,
AppOpsManager.opToPublicName(op),
resolvedAttributionSource, skipCurrentFinish);self 与 proxy 分别调用 finishOp/finishProxyOp。完成后从 sRunningAttributionSources 移除注册 token,释放 attribution 生命周期。
7. 分层边界
7.1 静态检查
PermissionManagerService.checkPermission 与 PermissionService 只读取 permission flags;AppOpsManager.noteOp 等操作由 PermissionChecker 在数据访问场景触发。不要在 Settings 中看到 permission granted 就断言传感器 API 一定可用。
7.2 结果映射
| Permission flags | AppOps mode | PermissionChecker 结果 | 常见服务行为 |
|---|---|---|---|
| denied | 任意 | hard denied | 抛异常/拒绝 |
| granted | allowed/default | granted | 访问允许 |
| granted | ignored | soft denied(runtime)或 hard denied(appop) | 空结果/拒绝 |
| granted | errored | hard denied | 拒绝/异常 |
实际 API 可能进一步检查前台状态、设备策略和 attribution;表格描述的是 PermissionChecker 层结果,不是所有资源服务的最终实现。
8. 验证方法
8.1 权限与 op
授予 CAMERA runtime permission,分别设置 android:camera AppOps 为 allowed、ignored、errored。普通 checkSelfPermission 应保持 granted,而 PermissionChecker/相机服务结果随 mode 变化。
8.2 Default fallback
将 AppOps mode 恢复 default,再撤销 permission。前者应回到 permission 层决定,后者应 hard denied。这个实验验证 MODE_DEFAULT 会继续调用 permission check,而不是直接允许。
8.3 attribution
构造合法 self attribution 和 proxy chain,分别执行 start/finish data delivery;再使用未注册中间节点,预期 hard denied 且已开始的 op 被 finish。检查 AppOps running 状态确认 token 不泄漏。
8.4 进程与缓存
保持 permission granted、切换 AppOps mode,调用真实 API 而非只读 pm check-permission。如果静态结果不变但 API 结果变化,说明变化发生在 AppOps/资源服务层。
adb shell pm check-permission <permission> <package.name> --user 0
adb shell appops get <package.name>
adb shell appops set <package.name> <op> ignore
adb shell appops set <package.name> <op> allow9. 源码路线
PermissionManagerService.PermissionCheckerService:permission、runtime 和 appop 分支。checkAppOpPermission:op 映射、mode 和 default fallback。checkRuntimePermission:permission 先行、soft/hard deny 和 attribution chain。performOpTransaction:self/proxy note/start 与 mode 计算。finishDataDelivery:失败回收和 attribution token 生命周期。AppOpsManager/AppOpsService:继续阅读 mode 持久化和用户策略。
AppOps 与权限不是重复系统:permission flags 说明“应用是否拥有资格”,AppOps mode 说明“这一次操作是否允许”,PermissionChecker 负责把两者和 attribution/data-delivery 生命周期组合起来。排查真实 API 权限问题时,必须同时观察 permission、op、user/device、attribution 和前台状态,不能只看一个 granted 位。
