卸载策略裁决
本文承接 用户应用卸载、多用户卸载 和 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。
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。
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 阶段失败。
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_MANAGER | device admin per user | 任一目标 user 命中即整次拒绝 |
DELETE_FAILED_OWNER_BLOCKED 且部分 user 已删除 | DELETE_ALL_USERS + blockUninstall users | 非阻止 user 已逐个执行删除 |
DELETE_FAILED_USER_RESTRICTED | DISALLOW_UNINSTALL_APPS | user policy 在 handler 前短路 |
DELETE_FAILED_USED_SHARED_LIBRARY | static/SDK library clients | 依赖保护先于实际删除 |
DELETE_FAILED_FOR_CHILD_PROFILE | deleteAppWithParent 与 child return code | parent 成功后 child 传播失败 |
| protected package internal error | ProtectedPackages.isPackageDataProtected | 与 device admin 返回码不同 |
| 策略通过但进程仍短暂运行 | freezer 和 kill mode | 冻结/杀进程是后续阶段,可能异步 |
11. 源码练习
- 构造三个 user,其中一个
blockUninstall=true,执行DELETE_ALL_USERS,追踪两个 user 的deletePackageX调用和最终返回码。 - 对比 device admin、protected data、user restriction 和 blockUninstall,列出它们的检查范围、返回码和是否允许部分删除。
- 给定包提供 SDK library 且所有 client optional,结合
sdkLibIndependence判断何时允许删除 host。 - 追踪 updated system app 删除后的
priorUserStates恢复,说明为什么mPackages仍可能保留 system 分区版本。
