Skip to content

数据清理流程

解释卸载时应用数据、ART profile、权限、域名和 keystore 的清理顺序与 flags。

基于android-17.0.0_r1
AndroidPackageManagerService卸载AppDataHelper源码阅读

数据清理流程 ​

本文承接 用户应用卸载。上一篇决定“删除哪个用户、是否保留包设置”;本文只追踪删除决定落地后的数据清理: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 BootAppDataHelper/installd
external data应用专属 external 目录Media/应用AppDataHelper
ART profileART profile 服务ART 编译ArtManagerLocal
PMS 状态域名、首选项、权限、AppsFilter系统查询RemovePackageHelper
Keystore namespaceKeystore daemon应用密钥AndroidKeyStoreMaintenance

清理路径由 RemovePackageHelper 编排,底层数据目录由 AppDataHelper 和 Installer/installd 执行。APK code path 的删除由 CleanUpArgs 负责,不能把它和应用数据目录当成同一资源。

2. 清理入口 ​

2.1 清理入口 ​

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

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 ​

java
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

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

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

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 单用户分支 ​

java
} 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 ​

java
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 异步化 ​

java
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

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 完全移除 ​

java
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. 源码路线 ​

  1. RemovePackageHelper.clearPackageStateForUserLIF:理解 flags 和清理顺序。
  2. AppDataHelper.destroyAppDataLIF / clearKeystoreData:跟踪 installd 与异步 keystore。
  3. destroyAppProfilesLIF:理解 ART 初始化和竞态容错。
  4. USER_ALL/单用户分支:区分全局域名、AppsFilter、KeySet 与 per-user 权限。
  5. removePackageDataLIF:跟踪 shouldDeletePackageSetting、mPackages 和 appId。
  6. DeletePackageHelper.deleteInstalledPackageLIF:继续追踪 code path 的 CleanUpArgs 删除。

数据清理的关键不是“调用 destroyAppData”,而是按 flags、用户范围和包设置生命周期分阶段处理:个人数据、ART profile、PMS 状态、权限、keystore 和 APK 代码各有 owner 与生效时机。只有把这些动作串起来,才能解释保留数据卸载、archive、单用户删除和系统 UID 等看似例外的行为。