权限类型
本文承接 权限框架 和 权限服务架构。Android 源码中至少有三套容易混淆的“权限类型”:PermissionInfo.protectionLevel 决定授予规则,PermissionInfo.flags 描述 restricted/removed 等定义属性,PermissionFlags 记录某个 appId、用户和设备上的实际授权状态。本文从 Android 17 的 Permission.kt 与 AppIdPermissionPolicy.kt 出发,解释 normal、dangerous、signature、internal 如何进入状态算法,以及 privileged、role、knownSigner、appop 等 flags 为什么不能脱离基础级别单独理解。
1. 三层模型
1.1 定义规则
protectionLevel 是基础 protection 与 protection flags 的位组合。例如 signature|privileged 的基础类型仍是 signature,privileged 只是增加另一条可能的授予条件。
1.2 定义元数据
PermissionInfo.flags 包含 hard restricted、soft restricted、removed、immutably restricted、Private Compute Core 等属性。它们不直接表示某个应用当前已获授权。
1.3 授权状态
PermissionFlags 保存 INSTALL_GRANTED、PROTECTION_GRANTED、RUNTIME_GRANTED、USER_SET、POLICY_FIXED、RESTRICTION_REVOKED 等状态。真实 check 读取这些 flags,而不是每次重新比较 Manifest 字符串。
2. Permission 对象
2.1 数据结构
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/Permission.kt
data class Permission(
val permissionInfo: PermissionInfo,
val isReconciled: Boolean,
val type: Int,
val appId: Int,
val gids: IntArray = EmptyArray.INT,
val areGidsPerUser: Boolean = false,
) {
inline val name: String
get() = permissionInfo.name
inline val packageName: String
get() = permissionInfo.packageName
inline val protection: Int
get() = permissionInfo.protection
inline val protectionFlags: Int
get() = permissionInfo.protectionFlags
}Permission 是系统级定义对象,不是某个应用的 grant。appId 表示定义该权限的包身份;gids 表示获得权限后附带的 Linux group;isReconciled 表示定义已经与当前包事实协调。每用户授权仍存放在 AccessState 的 permission flags 中。
2.2 定义来源
companion object {
// 权限由应用 Manifest 定义
const val TYPE_MANIFEST = 0
// 权限通过动态 API 定义
const val TYPE_DYNAMIC = 2
}Android 17 的现代数据类只有 Manifest 和 dynamic 两个 type。系统配置中的 permission/GID/allowlist 会作为 AccessPolicy.initialize 输入构建系统状态,但不再表现为旧 Java 模型中的独立 TYPE_CONFIG 常量。定义来源与 protection 类型是正交轴:动态权限也有自己的 protectionLevel。
2.3 GID 消费
fun getGidsForUser(userId: Int): IntArray =
if (areGidsPerUser) {
IntArray(gids.size) { index ->
UserHandle.getUid(userId, gids[index])
}
} else {
gids.copyOf()
}权限可以附加全局 GID 或按用户映射的 GID。PermissionService 汇总某个 UID 的 granted permissions 后计算最终 supplementary groups,随后由进程创建路径消费。用户级 GID 不能直接把配置中的整数原样传给所有用户。
3. Protection 位
3.1 基础级别
源码文件:frameworks/base/core/java/android/content/pm/PermissionInfo.java
public static final int PROTECTION_NORMAL = 0;
public static final int PROTECTION_DANGEROUS = 1;
public static final int PROTECTION_SIGNATURE = 2;
@Deprecated
public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
public static final int PROTECTION_INTERNAL = 4;
public static final int PROTECTION_MASK_BASE = 0xf;
public int getProtection() {
return protectionLevel & PROTECTION_MASK_BASE;
}
public int getProtectionFlags() {
return protectionLevel & ~PROTECTION_MASK_BASE;
}低四位是基础 protection,其余位是附加 flags。Permission.kt 的 isNormal/isRuntime/isSignature/isInternal 都调用已拆分的 permissionInfo.protection,不会把 flags 混进基础判断。
3.2 归一化
public static int fixProtectionLevel(int level) {
if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
level = PROTECTION_SIGNATURE
| PROTECTION_FLAG_PRIVILEGED;
}
if ((level & PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0
&& (level & PROTECTION_FLAG_PRIVILEGED) == 0) {
level &= ~PROTECTION_FLAG_VENDOR_PRIVILEGED;
}
return level;
}废弃的 signatureOrSystem 被转换为 signature|privileged。vendorPrivileged 不能脱离 privileged 单独存在,否则归一化会丢弃它。因此文章或工具展示 Manifest 原字符串时,不能假设它与策略层最终整数完全相同。
4. Normal
4.1 安装状态
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
if (permission.isNormal) {
val wasInstallGranted =
oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED)
val wasInstallRevoked =
oldFlags.hasBits(PermissionFlags.INSTALL_REVOKED)
var newFlags =
if (wasInstallGranted || !wasInstallRevoked) {
PermissionFlags.INSTALL_GRANTED
} else {
val requestedByInstalledPackage =
installedPackageState != null &&
permissionName in installedPackageState
.androidPackage!!.requestedPermissions
val requestedBySystemPackage =
requestingPackageStates.anyIndexed { _, it ->
it.isSystem
}
val compatibilityPermission =
requestingPackageStates.anyIndexed { _, it ->
isCompatibilityPermissionForPackage(
it.androidPackage!!, permissionName
)
}
if (requestedByInstalledPackage ||
requestedBySystemPackage ||
compatibilityPermission
) {
PermissionFlags.INSTALL_GRANTED
} else {
PermissionFlags.INSTALL_REVOKED
}
}
setPermissionFlags(appId, userId, permissionName, newFlags)
}normal 并非每次发现声明就无条件 grant。对于已安装的非系统包,平台后来新增的普通权限不能在没有应用更新的情况下悄悄授予;INSTALL_REVOKED 记录上次安装时的拒绝。包重新安装/更新、系统包或兼容权限才可能转为 INSTALL_GRANTED。
4.2 Purpose 限制
Android 17 在 feature flag 启用时,如果 permission 要求 purpose,而请求包没有声明有效 purpose,即使已有 INSTALL_GRANTED 也会叠加 PURPOSE_REVOKED。二进制授权结果由多个 flags 共同决定,看到一个 granted 来源位不能停止分析。
5. 静态权限
5.1 静态授予
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
} else if (permission.isSignature || permission.isInternal) {
val wasGranted =
oldFlags.hasBits(PermissionFlags.PROTECTION_GRANTED)
var newFlags =
if (hasMissingPackage && wasGranted) {
PermissionFlags.PROTECTION_GRANTED
} else {
val mayGrantByPrivileged =
!permission.isPrivileged ||
requestingPackageStates.anyIndexed { _, state ->
checkPrivilegedPermissionAllowlistIfNeeded(
state, permission
)
}
val bySignature = permission.isSignature &&
requestingPackageStates.anyIndexed { _, state ->
shouldGrantPermissionBySignature(state, permission)
}
val byProtectionFlags =
requestingPackageStates.anyIndexed { _, state ->
shouldGrantPermissionByProtectionFlags(
state, permission
)
}
if (mayGrantByPrivileged &&
(bySignature || byProtectionFlags)
) {
PermissionFlags.PROTECTION_GRANTED
} else {
0
}
}
setPermissionFlags(appId, userId, permissionName, newFlags)
}signature 与 internal 共用静态 protection 分支,但 signature 可以通过证书关系授予,internal 通常依赖 protection flags 指定的系统角色。若 permission 同时是 privileged,还必须先通过 privileged allowlist;“平台签名”并不自动绕过所有附加条件。
5.2 动态来源
if (permission.isDevelopment) {
newFlags = newFlags or
(oldFlags and PermissionFlags.RUNTIME_GRANTED)
}
if (permission.isRole) {
newFlags = newFlags or
(oldFlags and
(PermissionFlags.ROLE or
PermissionFlags.RUNTIME_GRANTED))
}development 和 role 属于动态授予来源,即便 privileged 静态条件未满足,也可能保留相应 runtime/role flags。源码特意纠正了旧实现中“暂时授予、下次 reconcile 又撤销”的不稳定行为。
6. Protection flags
6.1 特权与 OEM
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
when {
permission.isPrivileged -> {
if (packageState.isPrivileged) {
if ((packageState.isVendor || packageState.isOdm) &&
!permission.isVendorPrivileged
) {
return false
}
return true
}
}
permission.isOem -> {
if (packageState.isOem) {
val allowlistState = permissionAllowlist
.getOemAppAllowlistState(
packageName, permissionName
)
checkNotNull(allowlistState) {
"OEM permission must be explicitly declared"
}
return allowlistState
}
}
}privileged 需要包本身位于 privileged 分区并通过 allowlist;vendor/odm privileged 包还要求 permission 带 vendorPrivileged。OEM 权限必须在 OEM allowlist 中显式写 granted 或 denied,缺失配置是系统构建错误,而不是默认拒绝后继续。
6.2 Known package
if (permission.isInstaller &&
(packageName in knownPackages[PACKAGE_INSTALLER]!! ||
packageName in knownPackages[PACKAGE_PERMISSION_CONTROLLER]!!)
) return true
if (permission.isVerifier &&
packageName in knownPackages[PACKAGE_VERIFIER]!!
) return true
if (permission.isSetup &&
packageName in knownPackages[PACKAGE_SETUP_WIZARD]!!
) return true
if (permission.isModule && packageState.apexModuleName != null) {
return true
}installer、verifier、setup、recents、companion、configurator 等 flags 通过 knownPackages 或 APEX module 身份授予。判断的是系统当前选定的角色包,不是任意包名恰好包含 installer 字样。
6.3 Known signer
if (permission.isKnownSigner &&
androidPackage.signingDetails
.hasAncestorOrSelfWithDigest(permission.knownCerts)
) {
return true
}knownSigner 使用证书摘要集合和 signing lineage,允许多个受信签名者获得权限。它与 signature 基础类型的“和定义包证书关系”不同:信任集合直接来自 permission 定义中的 knownCerts。
7. Dangerous
7.1 Runtime flags
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
} else if (permission.isRuntime) {
var newFlags = oldFlags and PermissionFlags.MASK_RUNTIME
val wasRevoked = newFlags != 0 &&
!PermissionFlags.isPermissionGranted(newFlags)
val targetSdkVersion = requestingPackageStates.reduceIndexed(
Build.VERSION_CODES.CUR_DEVELOPMENT
) { target, _, packageState ->
target.coerceAtMost(
packageState.androidPackage!!.targetSdkVersion
)
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
if (permission.isRuntimeOnly) {
newFlags = newFlags and PermissionFlags.MASK_EXEMPT
} else {
newFlags = newFlags or PermissionFlags.LEGACY_GRANTED
if (wasRevoked) {
newFlags = newFlags or
PermissionFlags.APP_OP_REVOKED
}
}
}
}dangerous 对应 isRuntime,但最终 grant 由 runtime flags 决定。targetSdk 小于 M 的旧应用获得 LEGACY_GRANTED;runtimeOnly 明确禁止这类兼容 grant,只保留 restriction exemption。shared UID 的 targetSdk 取请求该权限包中的最小值,保证旧成员的兼容行为不被新成员覆盖。
7.2 隐式授权
平台拆分权限时,新 runtime permission 可能对旧 target 应用是 implicit。策略通过 IMPLICIT_GRANTED 记录兼容来源;应用升级 target 后若不再 implicit,会移除该来源,并在没有 fixed/用户保留条件时撤销 runtime grant。兼容授权不是永久用户选择。
7.3 Restricted
val isExempt = if (permission.isHardOrSoftRestricted &&
!wasExempt && !wasRestricted
) {
newFlags = newFlags or PermissionFlags.UPGRADE_EXEMPT
true
} else {
wasExempt
}
newFlags = if (permission.isHardRestricted && !isExempt) {
newFlags or PermissionFlags.RESTRICTION_REVOKED
} else {
newFlags andInv PermissionFlags.RESTRICTION_REVOKED
}hard restricted 没有 exemption 时设置 RESTRICTION_REVOKED;soft restricted 还会结合 targetSdk 和包条件决定 SOFT_RESTRICTED。安装器 allowlist、upgrade exemption 与系统固定状态都是 flags 来源,不改变 permission 的 dangerous 基础类型。
8. PermissionFlags
8.1 授权来源
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
object PermissionFlags {
const val INSTALL_GRANTED = 1 shl 0
const val INSTALL_REVOKED = 1 shl 1
const val PROTECTION_GRANTED = 1 shl 2
const val ROLE = 1 shl 3
const val RUNTIME_GRANTED = 1 shl 4
const val USER_SET = 1 shl 5
const val USER_FIXED = 1 shl 6
const val POLICY_FIXED = 1 shl 7
const val SYSTEM_FIXED = 1 shl 8
const val PREGRANT = 1 shl 9
const val LEGACY_GRANTED = 1 shl 10
const val IMPLICIT_GRANTED = 1 shl 11
}这些位同时记录“为什么 granted”和“谁限制了修改”。例如 RUNTIME_GRANTED|USER_SET 表示用户明确授予;PREGRANT|SYSTEM_FIXED 表示系统默认授予且用户不能修改;LEGACY_GRANTED|APP_OP_REVOKED 表示 permission 层兼容授予,但 AppOps 层拒绝真实访问。
8.2 二进制结果
真实 isPermissionGranted(flags) 会综合 granted 来源与 revoked/restriction/purpose 位,而不是检测任意一个 *_GRANTED。因此 dumpsys 中同时出现 granted 与 revoked 来源不是矛盾,后者可能覆盖前者形成最终 denied。
9. AppOp flag
9.1 定义与状态
Permission.isAppOp 只表示 permission 关联 AppOp。AppIdPermissionPolicy 在 normal、signature/internal 分支会保留 ROLE/USER_SET 等与 AppOp 相关的旧 flags;真正访问由 PermissionCheckerService 再查询 AppOps mode。permission granted 与数据访问 allowed 是两个结论。
9.2 Soft denied
AppOps MODE_IGNORED 常映射为 soft denied,让 API 返回空结果或降级行为;MODE_ERRORED 则可能 hard denied/抛异常。只执行 pm check-permission 无法覆盖 AppOps 层,必须结合 appops get 或真实 API 调用。
10. 未知与缺失
10.1 定义不存在
val permission = newState.systemState.permissions[permissionName]
if (permission == null) {
if (oldFlags == 0) {
setPermissionFlags(
appId, userId, permissionName,
PermissionFlags.INSTALL_REVOKED,
)
}
return
}若应用先请求一个尚未定义的权限,策略写入 INSTALL_REVOKED,防止未来某包定义同名 normal permission 时自动授予既有应用。缺失定义不是“等待后默认 granted”。
10.2 Missing package
shared UID 中某个 AndroidPackage 暂时缺失时,signature/internal 已有 PROTECTION_GRANTED 可以保留,避免存储卷未挂载导致静态权限抖动;单包且唯一 package 缺失时则跳过整次评估。这是 volume 生命周期的恢复策略,不适用于伪造缺失包。
11. 判定路线
12. 验证方法
12.1 Normal 更新
先安装一个没有请求某 normal permission 的 APK,再通过平台更新新增该权限定义但不更新应用。观察既有包是否保持 INSTALL_REVOKED;更新 APK 并声明该权限后才应重新评估。这验证 normal 并非定义出现即自动授予。
12.2 静态授权
构造同签名包、不同签名包、priv-app allowlisted 包和未 allowlist 包,请求同一个 signature|privileged permission。分别检查 PROTECTION_GRANTED。vendor priv-app 对不带 vendorPrivileged 的权限应被拒绝。
12.3 Runtime 兼容
用 targetSdk 22 与当前 targetSdk 的两个包请求 dangerous permission。旧 target 包应出现 legacy grant 来源;runtimeOnly permission 不应因旧 target 获得同样 grant。升级 target 后观察 legacy/implicit flags 被迁移或撤销。
12.4 限制与 AppOps
对 hard restricted 权限移除 installer exemption,确认 RESTRICTION_REVOKED 覆盖 runtime grant;对 appop permission 保持 permission granted、将 AppOps 设为 ignored,确认静态检查与真实数据访问结果分离。
adb shell pm list permissions -f -g
adb shell dumpsys package <package.name>
adb shell appops get <package.name>13. 源码路线
PermissionInfo.getProtection/getProtectionFlags/fixProtectionLevel:理解位布局和规范化。Permission.kt:区分定义对象、来源 type、metadata 与 GID。AppIdPermissionPolicy.evaluatePermissionState:沿 normal、signature/internal、runtime 三个主分支阅读。shouldGrantPermissionByProtectionFlags:展开 privileged、known packages、known signer 和 module。PermissionFlags.kt:理解实际授权来源、固定和撤销位。PermissionService.isPermissionGranted:观察 flags 如何汇总成最终结果。PermissionCheckerService:继续跟踪 AppOps 与真实数据访问。
权限“类型”不是一个枚举就能解释的概念。基础 protection 决定主算法,protection flags 增加身份授予路径,PermissionInfo flags 增加定义限制,PermissionFlags 保存每个 appId/user 的历史与当前状态,AppOps 最后影响真实数据访问。只有按这五层阅读,才能解释为什么同样声明一个权限,不同包、用户、targetSdk、分区和策略会得到不同结果。
