Skip to content

权限服务架构

深入 PermissionManagerService 的 Binder 门面、LocalServices、委托、归因和一次性权限职责。

基于android-17.0.0_r1
AndroidPermissionManagerServicePermissionCheckerAttributionSource源码阅读

权限服务架构 ​

本文承接 权限框架,只分析 Android 17 的 Java PermissionManagerService。全景篇已经说明现代权限状态由 AccessCheckingService 和 Kotlin PermissionService 持有;本文回答门面层为什么仍然很大:它如何同时服务 Binder 调用和 system_server 内部调用,哪些方法只是委托,哪些逻辑必须留在门面,permission_checker 如何管理 attribution 数据访问,以及一次性权限、自动撤销和虚拟设备 ID 如何接入。读完后,读者应能判断一个权限问题应停留在门面层排查,还是继续进入 AccessState 实现层。

1. 类的角色 ​

1.1 字段所有权 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
public class PermissionManagerService extends IPermissionManager.Stub {
    private static final ConcurrentHashMap<IBinder, RegisteredAttribution>
            sRunningAttributionSources = new ConcurrentHashMap<>();

    private final Object mLock = new Object();
    private final PackageManagerInternal mPackageManagerInt;

    @GuardedBy("mLock")
    private final SparseArray<OneTimePermissionUserManager>
            mOneTimePermissionUserManagers = new SparseArray<>();

    private final AppOpsManager mAppOpsManager;
    private final Context mContext;
    private final PermissionManagerServiceInterface
            mPermissionManagerServiceImpl;
    private final AttributionSourceRegistry mAttributionSourceRegistry;

    @GuardedBy("mLock")
    private CheckPermissionDelegate mCheckPermissionDelegate;

    private HotwordDetectionServiceProvider
            mHotwordDetectionServiceProvider;
    private VirtualDeviceManagerInternal
            mVirtualDeviceManagerInternal;
}

字段可以分成四组:实现委托、包/AppOps 依赖、门面自有的临时状态、特殊执行环境。mPermissionManagerServiceImpl 持久化 permission flags;one-time session、attribution token 和 delegate 则由 Java 服务自己拥有。把所有字段都归入“权限数据库”会误判生命周期。

1.2 依赖图 ​

2. 创建与注册 ​

2.1 构造过程 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
PermissionManagerService(Context context,
        ArrayMap<String, FeatureInfo> availableFeatures) {
    PackageManagerService.invalidatePackageInfoCache(
            PackageMetrics
                    .INVALIDATION_REASON_PERMISSION_MANAGER_SERVICE_INIT);
    PermissionManager.disablePackageNamePermissionCache();

    mContext = context;
    mPackageManagerInt = LocalServices.getService(
            PackageManagerInternal.class);
    mAppOpsManager = context.getSystemService(AppOpsManager.class);
    mVirtualDeviceManagerInternal = LocalServices.getService(
            VirtualDeviceManagerInternal.class);
    mAttributionSourceRegistry = new AttributionSourceRegistry(context);

    final PermissionManagerServiceInternalImpl localService =
            new PermissionManagerServiceInternalImpl();
    LocalServices.addService(
            PermissionManagerServiceInternal.class, localService);
    LocalServices.addService(
            PermissionManagerInternal.class, localService);

    mPermissionManagerServiceImpl = LocalServices.getService(
            PermissionManagerServiceInterface.class);
}

构造时先失效包信息缓存,并禁用 package-name permission cache,但保留 checkPermission cache。随后注册内部接口,最后取得 AccessCheckingService 预先注册的实现。启动顺序要求 PermissionManagerServiceInterface 已经存在,否则门面没有真实下游。

2.2 Binder 服务 ​

java
public static PermissionManagerServiceInternal create(
        Context context,
        ArrayMap<String, FeatureInfo> availableFeatures) {
    final PermissionManagerServiceInternal existing =
            LocalServices.getService(
                    PermissionManagerServiceInternal.class);
    if (existing != null) {
        return existing;
    }

    PermissionManagerService service =
            (PermissionManagerService) ServiceManager.getService(
                    "permissionmgr");
    if (service == null) {
        service = new PermissionManagerService(
                context, availableFeatures);
        ServiceManager.addService("permissionmgr", service);
        ServiceManager.addService("permission_checker",
                new PermissionCheckerService(context));
    }
    return LocalServices.getService(
            PermissionManagerServiceInternal.class);
}

create() 是幂等入口:若 LocalService 已存在直接返回;否则注册两个 Binder 服务。permissionmgr 面向管理与静态查询,permission_checker 面向 attribution/AppOps 数据访问检查。二者共享内部权限服务,却不是同一个 Binder 协议。

3. 委托模型 ​

3.1 直接委托 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
public PermissionInfo getPermissionInfo(String permissionName,
        String packageName, int flags) {
    return mPermissionManagerServiceImpl.getPermissionInfo(
            permissionName, flags, packageName);
}

public void grantRuntimePermission(String packageName,
        String permissionName, String persistentDeviceId,
        int userId) {
    mPermissionManagerServiceImpl.grantRuntimePermission(
            packageName, permissionName,
            persistentDeviceId, userId);
}

public void revokeRuntimePermission(String packageName,
        String permissionName, String persistentDeviceId,
        int userId, String reason) {
    mPermissionManagerServiceImpl.revokeRuntimePermission(
            packageName, permissionName,
            persistentDeviceId, userId, reason);
}

权限信息、flag、listener、restricted allowlist、grant/revoke 等大部分 API 直接委托。门面仍可能调整参数顺序或把 Binder 容器转换成 Java collection,所以源码阅读时要核对接口签名,而不是假设一一透传。

3.2 门面逻辑 ​

以下功能不能归为简单委托:

功能门面职责下游职责
checkPermissionnull 兼容、delegate、device ID读取 AccessState
auto-revoke调用者/包可见性检查、AppOps mode安装参数与权限 flags
one-time permission按用户管理 session 对象最终 revoke 权限
attributionUID/包名/链防伪、token 生命周期PermissionChecker/AppOps 消费
package lifecycle校验 user、展开 USER_ALLAccessState 包事件
data deliveryBinder 服务、链和 running op权限 grant 与 AppOps mode

4. 内部接口 ​

4.1 内部适配 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
private class PermissionManagerServiceInternalImpl
        implements PermissionManagerServiceInternal {
    @Override
    public int checkPermission(String packageName,
            String permissionName, String persistentDeviceId,
            int userId) {
        return PermissionManagerService.this.checkPermission(
                packageName, permissionName,
                persistentDeviceId, userId);
    }

    @Override
    public void onPackageAdded(PackageState packageState,
            boolean isInstantApp, AndroidPackage oldPkg) {
        mPermissionManagerServiceImpl.onPackageAdded(
                packageState, isInstantApp, oldPkg);
    }
}

LocalService 不等于直接暴露实现对象。检查路径回到外层门面,从而保留 delegate 和特殊 UID 处理;包生命周期等内部事件则直接委托实现层。这种选择让 system_server 调用避免 Binder,但仍复用同一策略入口。

4.2 安装事件包装 ​

java
public void onPackageInstalled(AndroidPackage pkg,
        int previousAppId, PackageInstalledParams params,
        int rawUserId) {
    Objects.requireNonNull(pkg, "pkg");
    Objects.requireNonNull(params, "params");
    Preconditions.checkArgument(
            rawUserId >= UserHandle.USER_SYSTEM
                    || rawUserId == UserHandle.USER_ALL,
            "userId");

    mPermissionManagerServiceImpl.onPackageInstalled(
            pkg, previousAppId, params, rawUserId);

    final int[] userIds = rawUserId == UserHandle.USER_ALL
            ? getAllUserIds() : new int[] { rawUserId };
    for (int userId : userIds) {
        final int mode = params.getAutoRevokePermissionsMode();
        if (mode == AppOpsManager.MODE_ALLOWED
                || mode == AppOpsManager.MODE_IGNORED) {
            setAutoRevokeExemptedInternal(
                    pkg, mode == AppOpsManager.MODE_IGNORED,
                    userId);
        }
    }
}

门面负责参数不变量和 USER_ALL 展开后的 auto-revoke AppOps;PermissionService 负责 permission states 和 restricted allowlist。一个安装事件因此同时修改两个状态域,但 owner 清晰分开。

4.3 卸载用户竞态 ​

java
public void onPackageUninstalled(String packageName,
        int appId, PackageState packageState,
        AndroidPackage pkg,
        List<AndroidPackage> sharedUserPkgs,
        int userId) {
    if (userId != UserHandle.USER_ALL) {
        final int[] userIds = getAllUserIds();
        if (!ArrayUtils.contains(userIds, userId)) {
            Slog.w(LOG_TAG,
                    "Skipping onPackageUninstalled() for"
                            + " non-existent user " + userId);
            return;
        }
    }
    mPermissionManagerServiceImpl.onPackageUninstalled(
            packageName, appId, packageState,
            pkg, sharedUserPkgs, userId);
}

异步包清理可能晚于用户删除。LocalService 在委托前再次确认 user 是否仍存在,避免为已经删除的 user 写权限状态。USER_ALL 不做该单用户检查。

5. Check delegate ​

5.1 包装真实检查 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
public int checkPermission(String packageName,
        String permissionName, String persistentDeviceId,
        int userId) {
    if (packageName == null || permissionName == null) {
        return PackageManager.PERMISSION_DENIED;
    }

    final CheckPermissionDelegate delegate;
    synchronized (mLock) {
        delegate = mCheckPermissionDelegate;
    }
    if (delegate == null) {
        return mPermissionManagerServiceImpl.checkPermission(
                packageName, permissionName,
                persistentDeviceId, userId);
    }
    return delegate.checkPermission(
            packageName, permissionName,
            persistentDeviceId, userId,
            mPermissionManagerServiceImpl::checkPermission);
}

delegate 接收一个指向真实实现的回调,可以先改写身份、记录或模拟检查,再选择是否调用原始逻辑。它受 mLock 保护,但在锁外执行,避免 delegate 反向调用服务时死锁。

5.2 设置入口 ​

java
private void setCheckPermissionDelegateInternal(
        CheckPermissionDelegate delegate) {
    synchronized (mLock) {
        mCheckPermissionDelegate = delegate;
    }
}

public void setCheckPermissionDelegate(
        CheckPermissionDelegate delegate) {
    setCheckPermissionDelegateInternal(delegate);
}

设置能力只从内部接口暴露,不是普通应用 Binder API。测试、shell delegation 或系统代理可以使用它,但持久权限状态并未被修改;移除 delegate 后检查恢复真实结果。

6. 虚拟设备 ​

6.1 Device ID 归一化 ​

java
private String getPersistentDeviceId(int deviceId) {
    return deviceId == Context.DEVICE_ID_DEFAULT
            || mVirtualDeviceManagerInternal == null
            ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
            : mVirtualDeviceManagerInternal
                    .getPersistentIdForDevice(deviceId);
}

public int checkUidPermission(int uid,
        String permissionName, int deviceId) {
    final String persistentDeviceId =
            getPersistentDeviceId(deviceId);
    return mPermissionManagerServiceImpl.checkUidPermission(
            uid, permissionName, persistentDeviceId);
}

Binder API 接收临时 numeric device ID,实现层状态以 persistent device ID 为键。虚拟设备服务不可用或默认设备时统一映射到 default ID,防止重启后数字 ID 变化导致权限状态串位。

7. 自动撤销 ​

7.1 调用者校验 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
private boolean checkAutoRevokeAccess(
        AndroidPackage pkg, int callingUid) {
    final boolean privileged =
            mContext.checkCallingOrSelfPermission(
                    Manifest.permission
                            .WHITELIST_AUTO_REVOKE_PERMISSIONS)
                    == PackageManager.PERMISSION_GRANTED;
    final boolean installer =
            mPackageManagerInt.isCallerInstallerOfRecord(
                    pkg, callingUid);
    if (!privileged && !installer) {
        throw new SecurityException(
                "Caller must hold auto-revoke permission"
                        + " or be installer on record");
    }
    if (pkg == null || mPackageManagerInt.filterAppAccess(
            pkg, callingUid,
            UserHandle.getUserId(callingUid))) {
        return false;
    }
    return true;
}

installer 或特权调用者才可调整 auto-revoke exemption;包不可见时返回 false。注意源码先计算 isCallerInstallerOfRecord(pkg, callingUid),具体实现必须正确处理 nullable pkg,调用链不能擅自绕过包存在性检查。

7.2 AppOps 写入 ​

java
private boolean setAutoRevokeExemptedInternal(
        AndroidPackage pkg, boolean exempted,
        int userId) {
    final int uid = UserHandle.getUid(userId, pkg.getUid());
    final AttributionSource source = new AttributionSource(
            uid, pkg.getPackageName(), null);

    if (mAppOpsManager.checkOpNoThrow(
            AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
            source) != AppOpsManager.MODE_ALLOWED) {
        return false;
    }

    final long identity = Binder.clearCallingIdentity();
    try {
        mAppOpsManager.setMode(
                AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
                uid, pkg.getPackageName(),
                exempted ? AppOpsManager.MODE_IGNORED
                        : AppOpsManager.MODE_ALLOWED);
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
    return true;
}

用户已设置的 managed-by-installer mode 会阻止 installer 覆盖。真正的 exemption 由 AppOps mode 表达:ignored 表示不执行 unused auto revoke,allowed 表示正常参与。该状态不存放在 permission grant flags 中。

8. 一次性权限 ​

8.1 按用户管理器 ​

java
private OneTimePermissionUserManager
        getOneTimePermissionUserManager(int userId) {
    synchronized (mLock) {
        final OneTimePermissionUserManager existing =
                mOneTimePermissionUserManagers.get(userId);
        if (existing != null) {
            return existing;
        }
    }

    // 创建 Context 可能获取 PMS 锁,不能持有 mLock
    final OneTimePermissionUserManager created =
            new OneTimePermissionUserManager(
                    mContext.createContextAsUser(
                            UserHandle.of(userId), 0));

    synchronized (mLock) {
        final OneTimePermissionUserManager raced =
                mOneTimePermissionUserManagers.get(userId);
        if (raced != null) {
            return raced;
        }
        mOneTimePermissionUserManagers.put(userId, created);
    }
    created.registerUninstallListener();
    return created;
}

这是典型的 drop-lock-and-retry:创建 user context 时不能持有服务锁,否则可能与 PMS 锁形成死锁;重新拿锁后若其他线程已创建实例,就丢弃本次对象。每个用户 manager 独立追踪超时、进程死亡延迟和卸载事件。

8.2 Session 入口 ​

java
@EnforcePermission(
        Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
public void startOneTimePermissionSession(String packageName,
        int deviceId, int userId, long timeoutMillis,
        long revokeAfterKilledDelayMillis) {
    startOneTimePermissionSession_enforcePermission();
    Objects.requireNonNull(packageName);
    final long token = Binder.clearCallingIdentity();
    try {
        getOneTimePermissionUserManager(userId)
                .startPackageOneTimeSession(
                        packageName, deviceId, timeoutMillis,
                        revokeAfterKilledDelayMillis);
    } finally {
        Binder.restoreCallingIdentity(token);
    }
}

门面执行生成的 permission enforcement、参数检查和 identity 清除,再委托 per-user manager。session 结束最终触发权限撤销,但其计时状态不写入 AccessState permission flags。

9. Attribution ​

9.1 Server token ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
public IBinder registerAttributionSource(
        AttributionSourceState sourceState) {
    final Binder token = new Binder();
    mAttributionSourceRegistry.registerAttributionSource(
            new AttributionSource(sourceState).withToken(token));
    return token;
}

token 必须由 server 创建。客户端持有这个 foreign Binder 时,引用会保持 server 对象存活;如果 token 由客户端创建,Binder 引用传播和 GC 语义可能让 server 端过早失去登记。

9.2 防伪检查 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
final int callingUid = resolveUid(Binder.getCallingUid());
final int sourceUid = resolveUid(source.getUid());
if (sourceUid != callingUid
        && mContext.checkPermission(
                Manifest.permission.UPDATE_APP_OPS_STATS,
                -1, callingUid)
                != PackageManager.PERMISSION_GRANTED) {
    throw new SecurityException(
            "Cannot register attribution source for uid");
}

if (packageManagerInternal.getPackageUid(
        source.getPackageName(), 0, userId)
        != sourcePackageUid) {
    throw new SecurityException(
            "Cannot register attribution source for package");
}

final AttributionSource next = source.getNext();
if (next != null && next.getNext() != null
        && !isRegisteredAttributionSource(next)) {
    throw new SecurityException(
            "Cannot register forged attribution source");
}

注册会验证 calling UID、source UID、包名归属以及已有链 token。中间节点不能随意前插或追加伪造 attribution;hotword isolated UID 会先解析为 owner UID,PCC UID 也有专用映射。

9.3 Weak registry ​

registry 使用 WeakHashMap<IBinder, AttributionSource>,保存时把原 token 替换为 default token,避免 value 对 key 形成强引用环。客户端释放 server token 后,登记可随 GC 消失。getRegisteredAttributionSourceCount 为提高准确性甚至主动请求两次 GC,但它仍只适合作为诊断数量。

10. 数据访问检查 ​

10.1 Checker 服务 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

java
private static final class PermissionCheckerService
        extends IPermissionChecker.Stub {
    private static final ConcurrentHashMap<String, PermissionInfo>
            sPlatformPermissions = new ConcurrentHashMap<>();
    private static final AtomicInteger sAttributionChainIds =
            new AtomicInteger(0);

    public int checkPermission(String permission,
            AttributionSourceState source,
            String message, boolean forDataDelivery,
            boolean startDataDelivery,
            boolean fromDatasource, int attributedOp) {
        final int result = checkPermission(
                mContext,
                mPermissionManagerServiceInternal,
                permission,
                new AttributionSource(source),
                message, forDataDelivery, startDataDelivery,
                fromDatasource, attributedOp);
        if (startDataDelivery
                && result != PermissionChecker.PERMISSION_GRANTED
                && result != PermissionChecker.PERMISSION_SOFT_DENIED) {
            finishDataDelivery(...);
        }
        return result;
    }
}

service 缓存 platform runtime PermissionInfo,为 attribution chain 分配 ID,并在部分链已经 start 但最终失败时主动 finish,避免 AppOps 留下悬空运行记录。

10.2 AppOp 分叉 ​

java
PermissionInfo info = sPlatformPermissions.get(permission);
if (info == null) {
    try {
        info = context.getPackageManager()
                .getPermissionInfo(permission, 0);
        if (PLATFORM_PACKAGE_NAME.equals(info.packageName)
                || HealthConnectManager.isHealthPermission(
                        context, permission)) {
            sPlatformPermissions.put(permission, info);
        }
    } catch (PackageManager.NameNotFoundException e) {
        return PermissionChecker.PERMISSION_HARD_DENIED;
    }
}
if (info.isAppOp()) {
    return checkAppOpPermission(...);
}

未知权限直接 hard denied;带 AppOp flag 的权限进入权限加 AppOps 的组合检查,其他权限走普通 permission chain。该层关心“本次数据传递”,不是修改持久 grant。

11. 失败与清理 ​

11.1 Delegate 异常 ​

delegate 运行在门面锁外;它抛出的异常会沿调用返回,不会修改 AccessState。排查测试授权异常时,应先确认 delegate 是否设置,再检查 PermissionService flags。

11.2 Attribution 断开 ​

数据访问结束时 finishDataDelivery 从 sRunningAttributionSources 移除 token 并调用 RegisteredAttribution.unregister();Binder death 也触发清理。否则 running AppOp 会持续存在,影响隐私指示和审计。

11.3 One-time 竞态 ​

并发首次请求可能创建多个临时 manager,但只有一个进入 map,其他实例被丢弃。卸载 listener 在成功插入后注册,保证包卸载可以停止对应 session;创建期间不持有 mLock 是避免死锁的必要条件。

11.4 Auto-revoke 拒绝 ​

调用者不是 installer/特权方时抛 SecurityException;包不可见返回 false;用户已控制 managed-by-installer AppOp 时也返回 false。三种失败含义不同,不能统一解释为“AppOps 写入失败”。

12. 验证方法 ​

12.1 调用路径 ​

分别通过公开 PermissionManager API 和 system_server 内部 PermissionManagerServiceInternal 调用同一 checkPermission。设置内部 delegate 后,两条路径都应经过门面包装;直接调用 implementation 的测试则不经过 delegate。这验证适配层边界。

12.2 虚拟设备 ​

在虚拟设备环境使用相同 uid/permission、不同 deviceId 检查权限,记录转换后的 persistent ID。默认设备或 VDM 未就绪时应使用 default ID;设备重建后持久 ID 相同的状态应可继续读取。

12.3 一次性 session ​

对两个用户启动相同包的一次性权限 session,分别终止进程和卸载包。断言每用户 manager 独立计时,进程死亡延迟后撤销,卸载 listener 清理 session;并发首次启动不应在 map 中留下重复 manager。

12.4 Attribution 防伪 ​

用与 calling UID 不匹配的 source UID、错误 package name、未注册的中间链分别注册,预期 SecurityException。合法链使用 server 返回 token 后,通过 PermissionChecker start/finish 数据访问,确认 running attribution 最终移除。

13. 源码路线 ​

  1. PermissionManagerService 字段和构造函数:先划分门面自有状态与实现状态。
  2. create():确认两个 Binder service 和两个 LocalService。
  3. 直接 delegate 方法与 PermissionManagerServiceInternalImpl:比较 Binder/内部调用路径。
  4. checkPermission / setCheckPermissionDelegateInternal:理解可插拔检查。
  5. auto-revoke 方法:追踪 PackageManager、包可见性与 AppOps。
  6. getOneTimePermissionUserManager:阅读 drop-lock-and-retry 并发模式。
  7. AttributionSourceRegistry:阅读 UID、包名、chain 和 weak token 校验。
  8. PermissionCheckerService:跟踪 permission、AppOps 与 data delivery 生命周期。

PermissionManagerService 的架构价值在于把不同调用环境压到同一权限实现之上,同时保留无法下沉的运行期协调:Binder 兼容、system_server 适配、delegate、device ID、one-time session、attribution 和 AppOps data delivery。遇到权限问题时,先判断它属于门面输入与生命周期,还是 AccessState 中的持久 permission flags,能够显著缩短源码定位路径。