Skip to content

AppOps 与权限

解释 Android 17 权限 flags 与 AppOps mode 如何共同决定真实数据访问结果。

基于android-17.0.0_r1
AndroidAppOpsPermissionCheckerPermissionService源码阅读

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

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

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 ​

java
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

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

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 信任链 ​

java
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

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 链 ​

java
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 flagsAppOps modePermissionChecker 结果常见服务行为
denied任意hard denied抛异常/拒绝
grantedallowed/defaultgranted访问允许
grantedignoredsoft denied(runtime)或 hard denied(appop)空结果/拒绝
grantederroredhard 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/资源服务层。

bash
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> allow

9. 源码路线 ​

  1. PermissionManagerService.PermissionCheckerService:permission、runtime 和 appop 分支。
  2. checkAppOpPermission:op 映射、mode 和 default fallback。
  3. checkRuntimePermission:permission 先行、soft/hard deny 和 attribution chain。
  4. performOpTransaction:self/proxy note/start 与 mode 计算。
  5. finishDataDelivery:失败回收和 attribution token 生命周期。
  6. AppOpsManager/AppOpsService:继续阅读 mode 持久化和用户策略。

AppOps 与权限不是重复系统:permission flags 说明“应用是否拥有资格”,AppOps mode 说明“这一次操作是否允许”,PermissionChecker 负责把两者和 attribution/data-delivery 生命周期组合起来。排查真实 API 权限问题时,必须同时观察 permission、op、user/device、attribution 和前台状态,不能只看一个 granted 位。