数据清理流程
本文承接 用户应用卸载。上一篇决定“删除哪个用户、是否保留包设置”;本文只追踪删除决定落地后的数据清理:CE/DE/external data 如何交给 AppDataHelper,DELETE_KEEP_DATA 和 archive 如何改变行为,USER_ALL 与单用户如何清理 PMS 状态,以及为什么 keystore 和 preferred activity 不是同一个同步步骤。源码基线为 Android 17 android-17.0.0_r1。
1. 清理对象
1.1 文件与状态
| 对象 | 典型位置/存储 | 主要消费者 | 清理 owner |
|---|---|---|---|
| CE data | /data/user/<id>/<pkg> | 应用解锁后运行 | AppDataHelper/installd |
| DE data | /data/user_de/<id>/<pkg> | Direct Boot | AppDataHelper/installd |
| external data | 应用专属 external 目录 | Media/应用 | AppDataHelper |
| ART profile | ART profile 服务 | ART 编译 | ArtManagerLocal |
| PMS 状态 | 域名、首选项、权限、AppsFilter | 系统查询 | RemovePackageHelper |
| Keystore namespace | Keystore daemon | 应用密钥 | AndroidKeyStoreMaintenance |
清理路径由 RemovePackageHelper 编排,底层数据目录由 AppDataHelper 和 Installer/installd 执行。APK code path 的删除由 CleanUpArgs 负责,不能把它和应用数据目录当成同一资源。
2. 清理入口
2.1 清理入口
源码文件:frameworks/base/services/core/java/com/android/server/pm/RemovePackageHelper.java
public void clearPackageStateForUserLIF(PackageSetting ps,
@CanBeALL @UserIdInt int userId, int flags) {
final String packageName = ps.getPackageName();
if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
mAppDataHelper.destroyAppProfilesLIF(packageName);
}
final AndroidPackage pkg;
final SharedUserSetting sus;
synchronized (mPm.mLock) {
pkg = mPm.mPackages.get(packageName);
sus = mPm.mSettings.getSharedUserSettingLPr(ps);
}
final AndroidPackage resolvedPkg = pkg != null
? pkg : PackageImpl.buildFakeForDeletion(
packageName, ps.getVolumeUuid());函数在 mInstallLock 保护的删除流程中调用,但读取 mPackages/shared user 仍需 mLock。存储设备被弹出时可能没有 AndroidPackage,源码会创建 fake package 仅用于让 installd 定位数据;这不表示包重新变成可运行对象。
2.2 数据 flags
int appDataDeletionFlags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL
| (flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
if ((flags & PackageManager.DELETE_ARCHIVE) != 0) {
mAppDataHelper.clearAppDataLIF(resolvedPkg, userId,
appDataDeletionFlags | Installer.FLAG_CLEAR_CACHE_ONLY);
mAppDataHelper.clearAppDataLIF(resolvedPkg, userId,
appDataDeletionFlags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
return;
}
mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId,
appDataDeletionFlags);没有 DELETE_KEEP_DATA 时销毁 DE、CE 和 external 数据;保留数据时直接返回,archive 例外是清理 cache 与 code cache。ART profile 在进入该分支前已经处理,除非显式携带 KEEP_ART_PROFILES。
3. AppDataHelper
3.1 数据方法
源码文件:frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java
@GuardedBy("mPm.mInstallLock")
public void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
final PackageSetting ps;
synchronized (mPm.mLock) {
ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
}
prepareAppDataPostCommitLIF(ps, 0, getInstalledUsersForPackage(ps));
}
public void clearKeystoreData(int userId, int appId) {
if (appId < 0) {
return;
}
for (int realUserId : mPm.resolveUserIds(userId)) {
AndroidKeyStoreMaintenance.clearNamespace(
Domain.APP, UserHandle.getUid(realUserId, appId));
}
}prepareAppDataAfterInstallLIF 是恢复/安装侧的反向动作;卸载侧使用 destroyAppDataLIF。Keystore 清理按 userId 展开 UID namespace,appId 无效时直接跳过。它由 background handler 异步调用,因此数据目录删除完成不代表密钥 namespace 同步完成。
3.2 ART profile
源码文件:frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java
public void destroyAppProfilesLIF(String packageName) {
if (!DexOptHelper.artManagerLocalIsInitialized()) {
// ART 尚未启动时,运行时会忽略陈旧 profile
return;
}
try (PackageManagerLocal.FilteredSnapshot snapshot =
getPackageManagerLocal().withFilteredSnapshot()) {
try {
DexOptHelper.getArtManagerLocal()
.clearAppProfiles(snapshot, packageName);
} catch (IllegalArgumentException e) {
// 包可能因竞态已不存在
Slog.w(TAG, e);
}
}
}profile 清理依赖 ART Service 是否已初始化;PMS 构造早期调用时允许跳过,因为 ART 会忽略陈旧 profile。包在并发删除中已经不存在时,IllegalArgumentException 只记录警告,不把整个卸载重新判为失败。
4. 全用户状态
4.1 USER_ALL 分支
源码文件:frameworks/base/services/core/java/com/android/server/pm/RemovePackageHelper.java
if (userId == UserHandle.USER_ALL) {
mPm.mDomainVerificationManager.clearPackage(packageName);
synchronized (mPm.mLock) {
mPm.mSettings.getKeySetManagerService()
.removeAppKeySetDataLPw(packageName);
mPm.mInjector.getUpdateOwnershipHelper()
.removeUpdateOwnerDenyList(packageName);
final Computer snapshot = mPm.snapshotComputer();
mPm.mAppsFilter.removePackage(snapshot,
snapshot.getPackageStateInternal(packageName));
final SparseBooleanArray changedUsers = new SparseBooleanArray();
mPm.clearPackagePreferredActivitiesLPw(
packageName, changedUsers, UserHandle.USER_ALL);
}
}全用户删除会清理全局域名验证、KeySet、更新所有权拒绝列表、AppsFilter 和所有用户的 preferred activities。AppsFilter.removePackage 使用 snapshot 读取当前包状态;这些动作必须在包设置仍可定位时完成。
4.2 单用户分支
} else {
mPm.mDomainVerificationManager.clearPackageForUser(
packageName, userId);
new PreferredActivityHelper(mPm, mBroadcastHelper)
.clearPackagePreferredActivities(packageName, userId);
final List<AndroidPackage> sharedUserPkgs = sus != null
? sus.getPackages() : Collections.emptyList();
mPermissionManager.onPackageUninstalled(packageName,
ps.getAppId(), ps, pkg, sharedUserPkgs, userId);
}单用户只清理该用户的域名验证、preferred activities 和权限状态,不移除全局 AppsFilter/KeySet。共享 UID 包列表传给权限服务,避免卸载一个包时误删同一 UID 下其他包仍需要的权限状态。
5. inode 与设置
5.1 数据目录 inode
mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId,
appDataDeletionFlags);
if (userId != UserHandle.USER_ALL) {
synchronized (mPm.mLock) {
ps.setCeDataInode(-1, userId);
ps.setDeDataInode(-1, userId);
ps.setPccCeDataInode(-1, userId);
ps.setPccDeDataInode(-1, userId);
}
}单用户目录销毁后,PackageSetting 中 CE/DE/PCC inode 被设为 -1。USER_ALL 不逐用户写这一段,因为后续包设置移除会结束这些记录;inode 是数据目录存在性的缓存,不是目录删除本身。
5.2 Keystore 异步化
if (ps.getAppId() == SYSTEM_UID) {
return;
}
mPm.mInjector.getBackgroundHandler().post(() -> {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
"clearKeystoreData:" + ps.getAppId()
+ " for user: " + userId);
try {
mAppDataHelper.clearKeystoreData(userId, ps.getAppId());
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
});共享 SYSTEM_UID 的包跳过 keystore namespace 清理,因为其他系统组件可能共用该 UID。普通包的 keystore 清理投递到 background handler,调用方不能用卸载回调到达作为密钥已经删除的证明。
6. 设置移除
6.1 设置是否删除
源码文件:frameworks/base/services/core/java/com/android/server/pm/RemovePackageHelper.java
final boolean shouldDeletePackageSetting =
shouldDeletePackageSetting(deletedPs, targetUserId,
allUserHandles, flags);
final AndroidPackage deletedPkg = deletedPs.getPkg();
clearPackageStateForUserLIF(deletedPs,
shouldDeletePackageSetting ? UserHandle.USER_ALL : targetUserId,
flags);
removePackageLI(packageName,
(flags & PackageManager.DELETE_CHATTY) != 0);
if (!deletedPs.isSystem()) {
deletedPs.setPkg(null);
}先清理数据和外围状态,再从 mPackages 移除运行对象。即使保留 PackageSetting,非系统包的 pkg 指针也会置空,避免后续查询误把它当作可运行 APK。
6.2 完全移除
if (shouldDeletePackageSetting) {
synchronized (mPm.mLock) {
outInfo.mIsAppIdRemoved =
mPm.mSettings.removePackageAndAppIdLPw(packageName);
if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
mPermissionManager.onPackageUninstalled(
packageName, deletedPs.getAppId(), deletedPs,
deletedPkg, sharedUserPkgs, USER_ALL);
}
mPm.mSettings.removeRenamedPackageLPw(
deletedPs.getRealName());
}
}只有 shouldDeletePackageSetting 为 true 才移除 appId、renamed package 和全局权限状态。系统更新保留 disabled system package 时,权限服务不能按普通“彻底消失”处理;这正是 system app 恢复路径与第三方完全删除的分界。
7. 顺序与失败
7.1 调用顺序
7.2 可恢复与不可恢复
ART profile 不可用时可以跳过;包因竞态不存在时 profile 清理只记录警告;但 destroyAppDataLIF、Settings 移除或系统包重扫失败可能影响卸载结果。keystore 投递失败通常不会回滚已经完成的包删除,因此需要单独查 background handler 日志。
8. 验证方法
8.1 完整删除
对第三方 APK 执行全用户卸载,检查 APK code path、CE/DE 数据、dumpsys package 中包设置和权限状态。关键断言是运行时包、包设置和数据目录都消失;keystore 需要通过独立密钥访问测试确认,不能只看卸载回调。
8.2 保留数据
执行 pm uninstall -k,确认核心数据仍在而包的 AndroidPackage 指针不再作为运行对象使用;archive 模式还应观察 cache/code-cache 被清理。重新安装同包名 APK,检查保留的 version/split 元数据是否仍参与校验。
8.3 单用户
在用户 0、10 都安装同一包,仅删除用户 0。断言用户 10 的 APK 和数据不受影响,用户 0 的 domain/preferred activity/permission 状态被清理,AppsFilter 等全局结构仍保留。
8.4 条件清理
在 ART Service 尚未初始化的启动阶段触发包清理,确认 profile 清理可跳过;对共享 SYSTEM_UID 的包确认 keystore 清理被跳过。两项验证覆盖源码中的条件 return,而不是普通 happy path。
9. 源码路线
RemovePackageHelper.clearPackageStateForUserLIF:理解 flags 和清理顺序。AppDataHelper.destroyAppDataLIF/clearKeystoreData:跟踪 installd 与异步 keystore。destroyAppProfilesLIF:理解 ART 初始化和竞态容错。- USER_ALL/单用户分支:区分全局域名、AppsFilter、KeySet 与 per-user 权限。
removePackageDataLIF:跟踪shouldDeletePackageSetting、mPackages和 appId。DeletePackageHelper.deleteInstalledPackageLIF:继续追踪 code path 的CleanUpArgs删除。
数据清理的关键不是“调用 destroyAppData”,而是按 flags、用户范围和包设置生命周期分阶段处理:个人数据、ART profile、PMS 状态、权限、keystore 和 APK 代码各有 owner 与生效时机。只有把这些动作串起来,才能解释保留数据卸载、archive、单用户删除和系统 UID 等看似例外的行为。
