权限服务架构
本文承接 权限框架,只分析 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
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
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 服务
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
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 门面逻辑
以下功能不能归为简单委托:
| 功能 | 门面职责 | 下游职责 |
|---|---|---|
checkPermission | null 兼容、delegate、device ID | 读取 AccessState |
| auto-revoke | 调用者/包可见性检查、AppOps mode | 安装参数与权限 flags |
| one-time permission | 按用户管理 session 对象 | 最终 revoke 权限 |
| attribution | UID/包名/链防伪、token 生命周期 | PermissionChecker/AppOps 消费 |
| package lifecycle | 校验 user、展开 USER_ALL | AccessState 包事件 |
| data delivery | Binder 服务、链和 running op | 权限 grant 与 AppOps mode |
4. 内部接口
4.1 内部适配
源码文件:frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.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 安装事件包装
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 卸载用户竞态
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
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 设置入口
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 归一化
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
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 写入
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 按用户管理器
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 入口
@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
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
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
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 分叉
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. 源码路线
PermissionManagerService字段和构造函数:先划分门面自有状态与实现状态。create():确认两个 Binder service 和两个 LocalService。- 直接 delegate 方法与
PermissionManagerServiceInternalImpl:比较 Binder/内部调用路径。 checkPermission/setCheckPermissionDelegateInternal:理解可插拔检查。- auto-revoke 方法:追踪 PackageManager、包可见性与 AppOps。
getOneTimePermissionUserManager:阅读 drop-lock-and-retry 并发模式。AttributionSourceRegistry:阅读 UID、包名、chain 和 weak token 校验。PermissionCheckerService:跟踪 permission、AppOps 与 data delivery 生命周期。
PermissionManagerService 的架构价值在于把不同调用环境压到同一权限实现之上,同时保留无法下沉的运行期协调:Binder 兼容、system_server 适配、delegate、device ID、one-time session、attribution 和 AppOps data delivery。遇到权限问题时,先判断它属于门面输入与生命周期,还是 AccessState 中的持久 permission flags,能够显著缩短源码定位路径。
