Skip to content

权限组

追踪 Android 17 权限组的解析、注册、owner 冲突和运行时状态影响。

基于android-17.0.0_r1
AndroidPermissionGroupPermissionServiceRuntime Permission源码阅读

权限组 ​

本文承接 权限定义与声明、权限类型 和 运行时权限请求。权限组不是只服务 UI 的分类表:Manifest 组会进入 AndroidPackage.permissionGroups,包加入时由 AppIdPermissionPolicy 注册到 AccessState,组 owner 或 runtime group 变化还会影响已有 permission flags。

1. 数据模型 ​

1.1 组和权限 ​

ParsedPermissionGroup 保存名称、label、icon、请求文案资源和 priority;PermissionInfo.group 只保存 permission 指向的组名。组没有整体 grant 位,组内 permission 仍按 appId、用户和设备保存 flags。

2. 注册路径 ​

2.1 包加入 ​

源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt

kotlin
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)
}

组注册、permission 注册、旧定义裁剪和 appId 重评估在同一 AccessState mutation 中完成;一个包的组变化可能影响其他包的 runtime 评估。

2.2 组写入 ​

kotlin
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 { _, parsedGroup ->
            val info = PackageInfoUtils.generatePermissionGroupInfo(
                parsedGroup, PackageManager.GET_META_DATA.toLong())!!
            newState.mutateSystemState()
                .mutatePermissionGroups()[info.name] = info
        }
}

只有所有用户都是 instant app 时才忽略组;普通包不能覆盖已有 owner,system 包可覆盖非 system owner,但不能覆盖另一个 system owner。

3. 定义变化 ​

3.1 组名关联 ​

源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/Permission.kt

kotlin
data class Permission(
    val permissionInfo: PermissionInfo,
    val isReconciled: Boolean,
    val type: Int,
    val appId: Int,
) {
    inline val groupName: String?
        get() = permissionInfo.group
}

permission 通过字符串关联组,不持有对象引用;组 owner 变化时可以按 name 重新解析。

3.2 定义变化 ​

源码文件:frameworks/base/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt

kotlin
val groupChanged = newPermissionInfo.isRuntime &&
    newPermissionInfo.group != oldPermission.groupName
val protectionChanged =
    (newPermissionInfo.isRuntime && !oldPermission.isRuntime) ||
        (newPermissionInfo.isInternal && !oldPermission.isInternal)
if (groupChanged || protectionChanged) {
    newState.externalState.userIds.forEachIndexed { _, userId ->
        newState.externalState.appIdPackageNames
            .forEachIndexed { _, appId, _ ->
                setPermissionFlags(appId, userId, permissionName, 0)
            }
    }
}

runtime permission 改组或 protection 变化时,所有 appId/user 旧 flags 清零后重算,避免组自动授予扩散旧语义。

4. 查询与消费 ​

4.1 查询 API ​

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

java
public ParceledListSlice<PermissionGroupInfo>
        getAllPermissionGroups(int flags) {
    return new ParceledListSlice<>(
            mPermissionManagerServiceImpl.getAllPermissionGroups(flags));
}

public PermissionGroupInfo getPermissionGroupInfo(
        String groupName, int flags) {
    return mPermissionManagerServiceImpl
            .getPermissionGroupInfo(groupName, flags);
}

Binder 门面包装查询结果;PermissionController 使用 group name、label、description、priority 组织 UI,但点击后仍逐 permission 调用 grant/revoke。

4.2 包移除 ​

onPackageRemoved 先 trim 不再声明的 group/permission,再按 appId 重新评估 shared UID 中剩余包。组删除不会自动删除仍被其他包声明的 permission。

5. 验证方法 ​

5.1 重复与 instant ​

两个普通包声明同名 group,后者应被忽略;system 包可接管非 system owner。所有用户均为 instant app 时组不应进入查询结果;存在 full-app 用户时可注册。

5.2 组变化 ​

更新 runtime permission 的 group,检查所有用户/appId 的 flags 被清零并重新评估;删除 group 定义后,其他包仍声明的 permission 不应消失。

bash
adb shell pm list permissions -f -g
adb shell dumpsys package <package.name>

6. 源码路线 ​

  1. ParsedPermissionGroup:解析字段和资源元数据。
  2. AppIdPermissionPolicy.onPackageAdded:注册与重评估。
  3. addPermissionGroups:instant app 和 owner 冲突。
  4. Permission.groupName:组与 permission 的字符串关联。
  5. group/protection change:flags 清零原因。
  6. trimPermissions/onPackageRemoved:卸载和 shared UID 裁剪。
  7. getAllPermissionGroups/getPermissionGroupInfo:查询和 UI 消费。

权限组只定义关联和展示,不拥有整体授权。Android 17 把组 definition、permission protection、appId/user flags 和 UI 查询分开管理;组可以影响展示与评估,却不能绕过每个 permission 的签名、fixed、restricted、role 和 AppOps 规则。