Skip to content

卸载策略裁决

追踪 Android 17 卸载前的用户范围、设备管理、保护包与 blockUninstall 裁决及部分成功语义。

基于android-17.0.0_r1
AndroidPMSDeletePackageHelperDevicePolicyMultiUser

卸载策略裁决 ​

本文承接 用户应用卸载、多用户卸载 和 ProtectedPackages 数据保护。它不重复删除数据、APK 和广播的完整流程,而是聚焦删除真正开始前的策略裁决:哪些检查会让整次请求失败,哪些只阻止某些 user,以及 DELETE_ALL_USERS 为什么可能返回失败但已经删除了部分用户。

1. 裁决入口 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java;符号:deletePackageVersionedInternal、deletePackageX。

卸载入口先规范化包名(处理 renamed package 和 static library),再根据 DELETE_ALL_USERS 得到目标用户数组。之后依次进行静态包/版本、静默卸载、跨用户、device admin、protected data、用户限制和 blockUninstall 检查;只有这些检查结束后,才把真正删除任务投递到 PMS handler。

2. 目标用户集合 ​

DELETE_ALL_USERS 为 true 时,users 是 UserManagerInternal.getUserIds() 的完整集合;否则只包含调用传入的 user。DELETE_ALL_USERS 不会跳过跨用户权限,也不意味着所有 user 必然能被删除。

更新系统应用的降级还有 admin user/profile parent 限制;非 admin 调用者在没有 DELETE_SYSTEM_APP 时会得到 DELETE_FAILED_USER_RESTRICTED。这发生在 blockUninstall 之前,不能用 blockUninstall 状态解释该失败。

3. 全局拒绝项 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java;源码符号:deletePackageVersionedInternal 的 identity-cleared 检查循环。

对目标 users 的任一 user,以下条件会直接终止整次请求:

  • 包是该 user 的 active device admin,返回 DELETE_FAILED_DEVICE_POLICY_MANAGER;
  • ProtectedPackages.isPackageDataProtected 为 true,回调 DELETE_FAILED_INTERNAL_ERROR;
  • 开启 required-system-package 保护且调用者无 ALLOW_CONTROL_SYSTEM_REQUIRED_PACKAGES,回调 internal error。
java
for (int user : users) {
    if (mPm.isPackageDeviceAdmin(packageName, user)) {
        observer.onPackageDeleted(packageName,
                DELETE_FAILED_DEVICE_POLICY_MANAGER, null);
        return;
    }
    if (mPm.mProtectedPackages.isPackageDataProtected(user, packageName)) {
        observer.onPackageDeleted(packageName,
                DELETE_FAILED_INTERNAL_ERROR, null);
        return;
    }
}

这些检查是“任一 user 命中则全局拒绝”,与后面的 blockUninstall 部分删除不同。判断使用清理后的 Binder identity,避免调用者包名或 UID 影响 DevicePolicy 查询。

4. 用户限制 ​

DISALLOW_UNINSTALL_APPS 以调用传入的 userId 检查。命中后异步回调 DELETE_FAILED_USER_RESTRICTED,不会进入 handler 的 deletePackageX。它是 user 级策略,不会读取每个包的 blockUninstall bit。

因此多用户请求需要区分:设备管理/保护包检查遍历全部目标 users;DISALLOW_UNINSTALL_APPS 先针对请求 user;DELETE_ALL_USERS 的后续 blockUninstall 再逐 user 裁决。

5. 阻止卸载 ​

如果不是 DELETE_ALL_USERS,且目标 user 的 blockUninstall 为 true,直接回调 DELETE_FAILED_OWNER_BLOCKED,不删除任何 user。blockUninstall 存在于 per-user package restrictions,和 ProtectedPackages 的 owner protection 不是同一个字段。

如果是 DELETE_ALL_USERS,helper 先收集阻止用户。

源码文件:frameworks/base/services/core/java/com/android/server/pm/DeletePackageHelper.java;符号:getBlockUninstallForUsers 与 deletePackageVersionedInternal。

java
int[] blockUninstallUserIds = getBlockUninstallForUsers(
        innerSnapshot, internalPackageName, users);
if (ArrayUtils.isEmpty(blockUninstallUserIds)) {
    returnCode = deletePackageX(internalPackageName,
            versionCode, userId, deleteFlags, false, callingUid);
} else {
    int userFlags = deleteFlags & ~PackageManager.DELETE_ALL_USERS;
    for (int userId1 : users) {
        if (!ArrayUtils.contains(blockUninstallUserIds, userId1)) {
            deletePackageX(internalPackageName, versionCode,
                    userId1, userFlags, false, callingUid);
        }
    }
    returnCode = DELETE_FAILED_OWNER_BLOCKED;
}

非阻止 user 会以去掉 DELETE_ALL_USERS 的 flags 单独删除;最后总回调仍报告 DELETE_FAILED_OWNER_BLOCKED。这个返回码描述“请求没有按全用户原子完成”,不代表零用户发生变化。

6. 子 Profile 传播 ​

单 user 删除成功后,helper 检查 child profiles。只有 parent 删除成功、child 仍 installed 且 UserProperties.getDeleteAppWithParent() 为 true 时,才对 child 调用 deletePackageX。child 失败会把父请求返回码改为 DELETE_FAILED_FOR_CHILD_PROFILE。

这条传播发生在 handler 内的真实删除之后;不能仅依据 parent 的 blockUninstall 状态判断 child 是否会跟随删除。

7. 冻结与删除 ​

通过所有策略后,deletePackageX 取得 mInstallLock,创建 PackageFreezer,再调用 deletePackageLIF。冻结保证删除期间目标进程不继续运行,但它不是策略检查;一个包可以通过策略而在冻结/Installer 阶段失败。

java
try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
    try (PackageFreezer freezer = mPm.freezePackageForDelete(
            packageName, freezeUser, deleteFlags,
            "deletePackageX", ApplicationExitInfo.REASON_OTHER)) {
        res = deletePackageLIF(packageName, UserHandle.of(removeUser),
                true, allUsers, deleteFlags, info, true, callingUid);
    }
}

冻结句柄关闭后才允许其他操作继续;若 DELETE_DONT_KILL_APP 设置,返回 stub freezer,策略检查仍然照常执行。

8. 共享库阻止 ​

deletePackageX 在冻结前检查目标是否提供 static shared library 或 SDK library。若当前 user 仍有 client 使用 required library,返回 DELETE_FAILED_USED_SHARED_LIBRARY。SDK library independence flag 只在所有 client 都声明 optional 时允许删除 host package。

这项检查位于 device admin/blockUninstall 之前的包级校验阶段,属于依赖关系保护,不是用户策略。排查“无法卸载库包”时先看 getPackagesUsingSharedLibrary,不要只看 restrictions。

9. 系统包恢复 ​

删除 updated system app 不是移除 system 分区 APK,而是删除更新版本并恢复 disabled system package。helper 保存所有 user 的 enabled state、last disable caller 和 installed 状态;恢复后逐 user 写回,并依据状态决定是否重新启用 compressed stub。

因此 system app 的删除可能返回成功,但 PMS 的 mPackages 仍有来自 system 分区的包。广播中的 removedForAllUsers、代码路径和 user installed 状态必须分别读取。

10. 失败定位 ​

现象检查顺序解释
DELETE_FAILED_DEVICE_POLICY_MANAGERdevice admin per user任一目标 user 命中即整次拒绝
DELETE_FAILED_OWNER_BLOCKED 且部分 user 已删除DELETE_ALL_USERS + blockUninstall users非阻止 user 已逐个执行删除
DELETE_FAILED_USER_RESTRICTEDDISALLOW_UNINSTALL_APPSuser policy 在 handler 前短路
DELETE_FAILED_USED_SHARED_LIBRARYstatic/SDK library clients依赖保护先于实际删除
DELETE_FAILED_FOR_CHILD_PROFILEdeleteAppWithParent 与 child return codeparent 成功后 child 传播失败
protected package internal errorProtectedPackages.isPackageDataProtected与 device admin 返回码不同
策略通过但进程仍短暂运行freezer 和 kill mode冻结/杀进程是后续阶段,可能异步

11. 源码练习 ​

  1. 构造三个 user,其中一个 blockUninstall=true,执行 DELETE_ALL_USERS,追踪两个 user 的 deletePackageX 调用和最终返回码。
  2. 对比 device admin、protected data、user restriction 和 blockUninstall,列出它们的检查范围、返回码和是否允许部分删除。
  3. 给定包提供 SDK library 且所有 client optional,结合 sdkLibIndependence 判断何时允许删除 host。
  4. 追踪 updated system app 删除后的 priorUserStates 恢复,说明为什么 mPackages 仍可能保留 system 分区版本。