Skip to content

安装权限处理

解释安装提交阶段如何构建权限参数,并由 PermissionManagerService 应用到包和用户状态。

基于android-17.0.0_r1
AndroidPackageManagerService权限安装源码阅读

安装权限处理 ​

本文承接 安装后注册、Session 安装 和 安装回调。重点不是重新介绍 Android 权限保护级别,而是追踪安装提交时的权限参数如何从 SessionParams 进入 InstallPackageHelper,再传给 PermissionManagerServiceInternal.onPackageInstalled。读完后,读者应能区分“请求授予”和“实际授予”、理解更新时权限为何继承、定位受限权限 allowlist 和权限模型降级的失败边界。

1. 权限输入 ​

1.1 三类输入 ​

安装权限决策同时读取三类数据:APK Manifest 中的 requested permissions,session 传入的显式权限状态,以及安装标志携带的批量授予/受限权限 allowlist。InstallRequest 只是把这些输入带到 commit;真正的权限状态 owner 是 PermissionManagerService。

1.2 标志和状态 ​

源码文件:frameworks/base/core/java/android/content/pm/PackageManager.java、frameworks/base/core/java/android/content/pm/PackageInstaller.java

java
public static final int INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS = 0x00000100;
public static final int INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS = 0x00400000;

public static final int PERMISSION_STATE_DEFAULT = 0;
public static final int PERMISSION_STATE_GRANTED = 1;
public static final int PERMISSION_STATE_DENIED = 2;

PERMISSION_STATE_* 是 session 对单个权限的请求,不是绕过保护级别的强制写入。INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS 让安装器请求把 Manifest 中所有权限交给权限服务处理;allowlist 标志则把 requested permissions 作为受限权限白名单候选。具体常量值以 Android 17 的 PackageManager.java 为准,不能沿用旧版本数字。

2. 参数载体 ​

2.1 参数对象 ​

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

java
final class PackageInstalledParams {
    public static final PackageInstalledParams DEFAULT = new Builder().build();

    private final ArrayMap<String, Integer> mPermissionStates;
    private final List<String> mAllowlistedRestrictedPermissions;
    private final int mAutoRevokePermissionsMode;

    PackageInstalledParams(ArrayMap<String, Integer> permissionStates,
            List<String> allowlistedRestrictedPermissions,
            int autoRevokePermissionsMode) {
        mPermissionStates = permissionStates;
        mAllowlistedRestrictedPermissions = allowlistedRestrictedPermissions;
        mAutoRevokePermissionsMode = autoRevokePermissionsMode;
    }
}

这是安装流程与权限服务之间的窄接口。它不携带 APK 路径、session id 或调用 UID;这些信息已经在更早的校验阶段完成。使用 DEFAULT 的安装表示没有额外权限状态请求,权限服务仍会按包声明、签名和既有用户状态执行默认逻辑。

2.2 Builder 写入 ​

java
final class Builder {
    private ArrayMap<String, Integer> mPermissionStates;
    private List<String> mAllowlistedRestrictedPermissions;
    private int mAutoRevokePermissionsMode = MODE_DEFAULT;

    Builder setPermissionStates(ArrayMap<String, Integer> states) {
        mPermissionStates = states;
        return this;
    }

    Builder setAllowlistedRestrictedPermissions(List<String> permissions) {
        mAllowlistedRestrictedPermissions = permissions;
        return this;
    }

    Builder setAutoRevokePermissionsMode(int mode) {
        mAutoRevokePermissionsMode = mode;
        return this;
    }

    PackageInstalledParams build() {
        return new PackageInstalledParams(mPermissionStates,
                mAllowlistedRestrictedPermissions,
                mAutoRevokePermissionsMode);
    }
}

Builder 的 owner 是安装线程,构建完成后参数供权限服务读取。它不会自己检查权限是否存在、是否属于 restricted 或是否可授予;这些判断必须由 PermissionManagerService 结合当前包和用户完成。

3. Commit 构建 ​

3.1 批量授予分支 ​

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

java
final PermissionManagerServiceInternal.PackageInstalledParams.Builder builder =
        new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
final boolean grantRequestedPermissions =
        (installRequest.getInstallFlags()
                & PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS) != 0;
if (grantRequestedPermissions) {
    final ArrayMap<String, Integer> states = new ArrayMap<>();
    for (String permissionName : pkg.getRequestedPermissions()) {
        states.put(permissionName,
                PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED);
    }
    builder.setPermissionStates(states);
} else {
    final ArrayMap<String, Integer> states = installRequest.getPermissionStates();
    if (states != null) {
        builder.setPermissionStates(states);
    }
}

批量标志优先于 session 的逐权限 map:设置了批量授予时,InstallPackageHelper 会为所有 requested permissions 构造 GRANTED 请求;未设置时才传递 session 的显式状态。这里仍然只是输入,PermissionManagerService 可以因保护级别、签名、用户限制或权限不存在而拒绝实际授予。

3.2 受限权限 ​

java
final boolean allowlistAllRestricted =
        (installRequest.getInstallFlags()
                & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
final List<String> allowlisted = allowlistAllRestricted
        ? new ArrayList<>(pkg.getRequestedPermissions())
        : installRequest.getAllowlistedRestrictedPermissions();
if (allowlisted != null) {
    builder.setAllowlistedRestrictedPermissions(allowlisted);
}

builder.setAutoRevokePermissionsMode(
        installRequest.getAutoRevokePermissionsMode());

allowlist 标志把整个 requested list 交给权限服务作为候选;没有该标志时,只传安装器明确列出的 allowlist。该列表不会自动把普通权限变成 granted,也不会替代 privileged permission XML 的系统白名单。

4. 权限服务消费 ​

4.1 内部接口 ​

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

java
/**
 * Callback when a package has been installed for a user.
 */
void onPackageInstalled(@NonNull AndroidPackage pkg,
        int previousAppId,
        @NonNull PackageInstalledParams params,
        @UserIdInt int userId);

previousAppId 只在包离开 shared UID 等场景提供旧 app id;普通安装使用 Process.INVALID_UID。userId 决定本次权限状态写入哪个用户,USER_ALL 由权限服务展开到所有适用用户。接口调用发生在包设置提交后,因此权限服务可以读取新的 AndroidPackage 和 PackageSetting。

4.2 调用顺序 ​

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

java
mPm.mPermissionManager.onPackageInstalled(
        pkg, installRequest.getPreviousAppId(),
        permissionParamsBuilder.build(), UserHandle.USER_ALL);

调用方是 InstallPackageHelper,消费者是 PermissionManagerService。前者负责把安装上下文翻译成权限参数,后者负责撤销已删除声明、继承可保留的旧 grant、授予 install 权限并更新 per-user 状态。调用返回不代表用户运行时权限 UI 已经弹出;它只完成安装阶段的权限初始化。

5. 首次安装与更新 ​

5.1 首次安装 ​

首次安装没有旧包权限状态可继承。权限服务依据 requested permissions、保护级别、签名和安装器传入的显式状态创建新的 uid permission state。normal/install 权限可能在安装阶段授予,dangerous 权限通常仍由运行时授权流程决定。

5.2 更新安装 ​

更新安装传入旧包对象和 previousAppId,权限服务可以识别同一包的旧状态。Manifest 删除的权限会被撤销;仍存在且兼容的 grant 可以保留;新增 dangerous 权限不会因为更新自动绕过用户授权。更新时的权限继承因此依赖包名、签名、旧 app id 和用户状态,而不是仅比较 APK versionCode。

5.3 权限模型降级 ​

安装前检查会拒绝把已支持运行时权限模型的包降级到旧 target SDK。错误码是 INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,失败发生在权限状态写入之前;因此不要把该错误和“某个 runtime permission 没有自动授予”混为一谈。

6. 失败边界 ​

6.1 请求不可授予 ​

PERMISSION_STATE_GRANTED 只是请求。权限不存在、声明方不匹配、签名不满足、restricted 权限没有 allowlist 或用户策略禁止时,PermissionManagerService 会拒绝或调整最终状态。安装可以成功完成,但某个权限保持 denied/default;调用方必须读取最终权限状态,而不能只检查 session 参数。

6.2 安装失败 ​

如果 parse、签名、reconcile 或 commit 失败,InstallPackageHelper 不会把未提交的权限参数当成持久状态。失败清理回收 code path、App ID 和临时设置;已存在的旧包权限状态仍由旧 PackageSetting 持有。

7. 验证方法 ​

7.1 显式状态 ​

创建 session,对同一个 APK 分别设置 CAMERA 为 GRANTED、DENIED 和 DEFAULT,再 commit。安装完成后用 pm check-permission 或 dumpsys package 查看用户状态。关键断言是参数状态会进入权限服务,但最终结果仍受保护级别和用户策略约束;这个实验不能证明普通应用能授予 signature 权限。

7.2 更新继承 ​

先安装请求一个 dangerous 权限的 v1,再更新到仍声明该权限的 v2,并删除另一个权限。断言仍声明且兼容的状态可能保留,已删除权限被撤销,新权限不会自动获得运行时授权。该实验覆盖更新路径,不覆盖 privileged allowlist。

7.3 受限权限 ​

在具备相应安装权限的测试环境中,分别使用和不使用 INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS 安装声明 restricted permission 的 APK。比较 dumpsys package 的最终 flags 和 permission state,确认 allowlist 是输入条件而非无条件 grant。权限不足时预期安装器在参数检查或权限服务阶段失败。

8. 源码路线 ​

  1. PackageInstaller.SessionParams.setPermissionState:确认调用方如何表达逐权限请求。
  2. InstallRequest:查看 permission states、allowlisted permissions 和 auto-revoke mode 的携带方式。
  3. InstallPackageHelper 提交段:跟踪批量授予与 allowlist 分支。
  4. PermissionManagerServiceInternal.PackageInstalledParams:理解安装器与权限服务的窄接口。
  5. PermissionManagerService.onPackageInstalled:跟踪声明校验、旧状态继承和 per-user 写入。
  6. 权限模型降级检查:区分安装失败和权限最终为 denied。

安装权限处理的主线是“安装器提供请求,InstallPackageHelper 组织参数,PermissionManagerService 决定最终状态”。把这三层分开,才能解释为什么设置了 GRANTED 仍可能被拒绝,也能准确定位更新、受限权限和权限模型降级问题发生在哪个阶段。