权限定义与声明
本文承接 权限类型 和 权限服务架构。上一文解释 protection 与授权 flags;本文回到 Manifest 输入端,追踪 <permission>、<permission-group>、<permission-tree> 如何成为 AndroidPackage 的解析结果,并在包加入、更新、卸载时进入 AppIdPermissionPolicy。<uses-permission> 只是请求,不拥有权限定义。
1. Manifest 输入
1.1 四种声明
<permission
android:name="com.example.permission.READ_RECORD"
android:protectionLevel="dangerous"
android:permissionGroup="com.example.permission-group.RECORDS" />
<permission-group android:name="com.example.permission-group.RECORDS" />
<permission-tree android:name="com.example.permission" />
<uses-permission android:name="com.example.permission.READ_RECORD" />permission 定义可被请求的权限;permission-group 提供分组元数据;permission-tree 定义动态权限前缀;uses-permission 进入 AndroidPackage.requestedPermissions。解析阶段只建立事实,是否授予由后续策略计算。
1.2 tree 匹配
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
fun GetStateScope.findPermissionTree(
permissionName: String
): Permission? =
state.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
_, treeName, tree ->
if (permissionName.startsWith(treeName) &&
permissionName.length > treeName.length &&
permissionName[treeName.length] == '.'
) tree else null
}动态权限必须是 tree name 加点号作为前缀。相似前缀但没有点号的名称不属于该 tree,避免跨包创建权限。
2. 包加入
2.1 生命周期入口
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
override fun MutateStateScope.onPackageAdded(
packageState: PackageState
) {
val changed = MutableIndexedSet<String>()
adoptPermissions(packageState, changed)
addPermissionGroups(packageState)
addPermissions(packageState, changed)
trimPermissions(packageState.packageName, changed)
trimPermissionStates(packageState.appId)
changed.forEachIndexed { _, name ->
evaluatePermissionStateForAllPackages(name, null)
}
evaluateAllPermissionStatesForPackage(
packageState, packageState
)
updatePermissionsOnPackageUpdate(packageState.appId)
}包加入会先 adoption,再加入组和权限,裁剪过时定义,最后重新评估所有受影响 appId。一个包定义权限,可能改变其他包的 protection/请求关系,因此不能只更新自己的 map。
2.2 权限组
private fun MutateStateScope.addPermissionGroups(
packageState: PackageState
) {
val allUsersInstant = packageState.userStates.allIndexed {
_, _, state -> state.isInstantApp
}
if (allUsersInstant) {
Slog.w(LOG_TAG,
"Ignoring permission groups declared by instant app")
return
}
packageState.androidPackage!!.permissionGroups
.forEachIndexed { _, parsed ->
val info = PackageInfoUtils.generatePermissionGroupInfo(
parsed, PackageManager.GET_META_DATA.toLong()
)!!
newState.mutateSystemState()
.mutatePermissionGroups()[info.name] = info
}
}instant app 不能声明 permission group。组冲突时,普通包不能覆盖已有 owner;system 包可以覆盖非 system 定义,但两个 system 包之间仍拒绝后者。
3. 权限注册
3.1 定义映射
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
private fun MutateStateScope.addPermissions(
packageState: PackageState,
changed: MutableIndexedSet<String>,
) {
val pkg = packageState.androidPackage!!
pkg.permissions.forEachIndexed { _, parsed ->
val info = PackageInfoUtils.generatePermissionInfo(
parsed, PackageManager.GET_META_DATA.toLong()
)!!
val old = if (parsed.isTree) {
newState.systemState.permissionTrees[info.name]
} else {
newState.systemState.permissions[info.name]
}
val tree = findPermissionTree(info.name)
if (tree != null && info.packageName != tree.packageName) {
Slog.w(LOG_TAG,
"Ignoring permission: tree owned by another package")
return@forEachIndexed
}
// old owner、protection 和状态变化在后续逻辑中处理
}
}generatePermissionInfo 把解析对象转换成系统 definition。tree 和普通 permission 使用不同 map,但都必须先通过 tree owner 检查。
3.2 owner 冲突
if (old != null && info.packageName != old.packageName) {
if (!packageState.isSystem) {
Slog.w(LOG_TAG,
"Ignoring permission already declared by another package")
return@forEachIndexed
}
if (old.isReconciled &&
newState.externalState.packageStates[
old.packageName
]?.isSystem == true
) {
Slog.w(LOG_TAG,
"Ignoring permission already declared by system package")
return@forEachIndexed
}
// system 包接管非 system 定义,清除所有旧 flags
newState.externalState.userIds.forEachIndexed { _, userId ->
newState.externalState.appIdPackageNames
.forEachIndexed { _, appId, _ ->
setPermissionFlags(appId, userId, info.name, 0)
}
}
}非 system 重复定义被忽略;system 接管时清除所有 appId/用户的旧状态,因为 protection、group 和签名 owner 已经改变。
3.3 定义变化
val groupChanged = info.isRuntime &&
info.group != old.groupName
val protectionChanged =
(info.isRuntime && !old.isRuntime) ||
(info.isInternal && !old.isInternal)
if (groupChanged || protectionChanged) {
newState.externalState.userIds.forEachIndexed { _, userId ->
newState.externalState.appIdPackageNames
.forEachIndexed { _, appId, _ ->
setPermissionFlags(appId, userId, info.name, 0)
}
}
}同一 owner 改变 runtime group 或 protection level 时,已有 grant 必须清零后重算;否则组自动授予或保护级别变化可能造成权限升级。
4. 动态权限
4.1 写入 API
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
fun MutateStateScope.addPermissionTree(permission: Permission) {
newState.mutateSystemState().mutatePermissionTrees()[permission.name] = permission
}
fun MutateStateScope.addPermission(
permission: Permission, isSynchronousWrite: Boolean = false,
) {
val mode = if (isSynchronousWrite) WriteMode.SYNCHRONOUS else WriteMode.ASYNCHRONOUS
newState.mutateSystemState(mode).mutatePermissions()[permission.name] = permission
}动态 API 也进入 AccessState 的 definition map。write mode 只影响落盘时机,不改变 tree owner 或 protection。
4.2 动态 owner
private fun MutateStateScope.updatePermissionIfDynamic(
permission: Permission
): Permission {
if (!permission.isDynamic) return permission
val tree = findPermissionTree(permission.name) ?: return permission
return permission.copy(
permissionInfo = PermissionInfo(permission.permissionInfo)
.apply { packageName = tree.packageName },
appId = tree.appId,
isReconciled = true,
)
}动态 permission 的最终 owner 来自 tree。tree 重新出现时,definition 会复制 tree 的包名和 appId。
5. 声明与评估
5.1 requested list
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
val packageNames = newState.externalState.appIdPackageNames[appId]!!
val requesting = MutableIndexedList<PackageState>()
packageNames.forEachIndexed { _, name ->
val state = newState.externalState.packageStates[name]!!
val pkg = state.androidPackage
if (pkg != null && permissionName in pkg.requestedPermissions) {
requesting += state
}
}shared UID 下,任一包的 uses-permission 都成为 appId 评估输入。没有声明的包不会因 definition 存在而自动获得权限。
5.2 未定义权限
val permission = newState.systemState.permissions[permissionName]
if (permission == null) {
if (oldFlags == 0) {
setPermissionFlags(appId, userId, permissionName,
PermissionFlags.INSTALL_REVOKED)
}
return
}请求先于定义出现时写入 INSTALL_REVOKED,防止未来同名 normal permission 出现后悄悄授予既有包。
6. 删除与裁剪
6.1 trim 定义
源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
private fun MutateStateScope.trimPermissions(
packageName: String,
changed: MutableIndexedSet<String>,
) {
val packageState = newState.externalState.packageStates[packageName]
val pkg = packageState?.androidPackage
if (packageState != null && pkg == null) return
newState.systemState.permissions.forEachReversedIndexed {
index, name, permission ->
val updated = updatePermissionIfDynamic(permission)
newState.mutateSystemState().mutatePermissions().putAt(index, updated)
if (updated.packageName == packageName &&
(packageState == null || pkg!!.permissions.noneIndexed {
_, parsed -> !parsed.isTree && parsed.name == name
})
) {
newState.mutateSystemState().mutatePermissions().removeAt(index)
changed += name
}
}
}包卸载或更新后不再声明的 permission 会被 trim,并触发所有受影响包重评估。动态 permission 若仍被 tree 所有,则先更新 owner。
6.2 包移除
override fun MutateStateScope.onPackageRemoved(
packageName: String, appId: Int
) {
check(packageName !in newState.externalState.disabledSystemPackageStates)
val changed = MutableIndexedSet<String>()
trimPermissions(packageName, changed)
if (appId in newState.externalState.appIdPackageNames) {
trimPermissionStates(appId)
evaluateAllPermissionStatesForAppId(appId)
}
changed.forEachIndexed { _, name ->
evaluatePermissionStateForAllPackages(name, null)
}
}移除包先裁剪定义,再按 appId 重新评估。shared UID 仍有其他包时,不会简单清空整个 appId 的权限状态。
7. 持久化与冲突
权限定义进入 AccessState.systemState.permissions/permissionTrees/permissionGroups;每用户 grant flags 进入 AccessState.userStates[userId].appIdPermissionFlags。AccessCheckingService.mutateState 在策略变更后调用 AccessPersistence.write(newState),因此定义和状态更新在同一 copy-on-write 事务中提交。
遇到权限声明但查不到时,按顺序检查解析后的 AndroidPackage.permissions、tree owner、同名 owner 冲突、trim 是否移除了 definition,最后才检查 appId/user flags。
8. 验证方法
8.1 重复定义
安装两个普通包声明同名 permission,预期后者被忽略;将后者置为 system 包时,它可以接管非 system owner,并清除所有 appId 的旧 flags。instant app 声明 permission-group 应被忽略。
8.2 权限树
创建合法 tree 子权限与相似错误前缀,前者必须归属 tree owner,后者不能匹配。卸载 tree owner 后,动态权限应在 trim/owner 更新路径中消失或转移。
8.3 定义变化
更新同一 permission 的 group 或 protection,已有用户 flags 应被清零并重新评估。
8.4 未定义请求
让包请求当前不存在的权限,确认 appId/user flags 先记录 INSTALL_REVOKED;定义包出现后仍需重新评估。
adb shell pm list permissions -f -g
adb shell dumpsys package <package.name>
adb shell pm check-permission <permission> <package.name> --user 09. 源码路线
ParsedPackage的 permission/group/tree 字段:确认 Manifest 解析输出。AppIdPermissionPolicy.onPackageAdded:跟踪 adoption、add、trim 和重评估。addPermissionGroups:阅读 instant app 与 owner 冲突。addPermissions:阅读 tree owner、重复 owner 和定义变化。findPermissionTree/updatePermissionIfDynamic:理解动态命名空间。evaluatePermissionStateForAppId:观察uses-permission如何进入 appId/user 状态。trimPermissions/onPackageRemoved:跟踪定义和 shared UID 状态裁剪。
权限定义与声明的主线是“Manifest 事实进入系统状态,再由策略评估请求者”:tree 决定命名空间 owner,system/non-system 规则决定冲突,trim 维护定义生命周期,uses-permission 才成为授予输入。
