应用更新流程
本文承接 安装全景、安装前检查、降级安装 和 系统应用更新。主题限定为普通 APK 的 replace install:新包如何确认自己可以接管旧包的数据,何时冻结旧进程,旧 PackageSetting 如何被新对象替换,以及更新失败时哪些状态仍保持不变。系统包的 disabled 记录和 OTA 恢复不在本文重复展开。
1. 更新对象
1.1 数据 owner
更新判断不是“目录中存在同名 APK”,而是 Settings.getPackageLPr(packageName) 返回已有 PackageSetting。它拥有 version、signing details、appId、per-user 状态和 code path;AndroidPackage 是当前可运行对象,可能因 DELETE_KEEP_DATA 而为空,但设置仍保留。
1.2 replace 标志
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
final String renamedPackage =
mPm.mSettings.getRenamedPackageName(pkgName);
if (renamedPackage != null) {
pkgName = renamedPackage;
}
synchronized (mPm.mLock) {
ps = mPm.mSettings.getPackageLPr(pkgName);
if (ps != null) {
replace = true;
}
}
}replace 是本次 request 的事实状态,后续签名、版本和删除旧包都围绕它展开。renamed package 先归一化,避免把历史重命名记录当成新安装。
2. 签名校验
2.1 调用方
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
final boolean compareCompat =
ReconcilePackageUtils.isCompatSignatureUpdateNeeded(
mPm.getSettingsVersionForPackage(parsedPackage));
final boolean compareRecover =
ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
mPm.getSettingsVersionForPackage(parsedPackage));
final boolean compatMatch =
PackageManagerServiceUtils.verifySignatures(
signatureCheckPs, signatureCheckSus, null,
parsedPackage.getSigningDetails(),
compareCompat, compareRecover, isRollback);InstallPackageHelper 根据 settings 版本决定是否尝试旧签名格式兼容和证书恢复,然后把已有包、shared UID 和新包 SigningDetails 交给工具函数。普通 replace 的 isRollback 为 false;Rollback 专题会额外放宽历史签名能力。
2.2 能力与 lineage
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
boolean match = parsedSignatures.checkCapability(
pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| pkgSetting.getSigningDetails().checkCapability(
parsedSignatures,
SigningDetails.CertCapabilities.ROLLBACK);
if (!match && compareCompat) {
match = matchSignaturesCompat(packageName,
pkgSetting.getSignatures(), parsedSignatures);
compatMatch = match;
}
if (!match && compareRecover) {
match = matchSignaturesRecover(packageName,
pkgSetting.getSigningDetails(), parsedSignatures,
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| matchSignaturesRecover(packageName, parsedSignatures,
pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK);
}
if (!match) {
throw new PackageManagerException(
INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Existing package " + packageName
+ " signatures do not match newer version; ignoring!");
}签名不是简单 byte equality:签名 lineage 中的 capability 决定新证书能否接管已安装数据;兼容/恢复分支只在 settings 版本要求时启用。失败抛出 INSTALL_FAILED_UPDATE_INCOMPATIBLE,发生在 commit 前,因此旧包仍是当前运行对象。
2.3 shared UID
若包属于 shared UID,verifySignatures 还会用 canJoinSharedUserId 对照 shared user 的 signing lineage;不匹配返回 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE。这条检查防止更新包以不兼容证书进入已有 UID,从而获得同 UID 其他包的数据和权限。
3. 版本与数据
3.1 版本检查
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
final Pair<Integer, String> versionCheck = verifyReplacingVersionCode(
pkgLite, request.getRequiredInstalledVersionCode(), installFlags);
if (versionCheck.first != PackageManager.INSTALL_SUCCEEDED) {
throw new PrepareFailure(versionCheck.first, versionCheck.second);
}普通更新默认禁止降级;requiredInstalledVersionCode 还可要求当前版本必须精确匹配,避免并发更新覆盖了调用方预期的旧版本。长版本号、base revision 和 split revision 的比较细节见 降级安装。
3.2 数据路径一致性
更新不会在 prepare 阶段销毁旧数据。旧 PackageSetting 的 CE/DE inode、appId 和 per-user 状态会被传给新设置;commit 后 AppDataHelper 为新 package 准备同一用户的数据目录。若 Manifest 改变 PROPERTY_NO_APP_DATA_STORAGE,checkNoAppStorageIsConsistent 会拒绝不一致的更新,避免包在“有数据/无数据存储”之间悄悄切换。
3.3 update owner
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
final String oldUpdateOwner = pkgAlreadyExists
? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null;
final boolean isUpdate = pkgAlreadyExists && oldPkgSetting.getInstalled(userId);
final boolean requestOwnership =
(request.getInstallFlags()
& PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP) != 0;
final boolean sameOwner = TextUtils.equals(
oldUpdateOwner, installSource.mInstallerPackageName);
if (!isUpdate || !requestOwnership || !sameOwner) {
installSource = installSource.setUpdateOwnerPackageName(null);
}
pkgSetting.setInstallSource(installSource);更新 owner 是安装来源状态的一部分,不等于签名 owner。源码会考虑当前用户是否已安装、installer 是否相同、是否请求 ownership 以及 denylist;installer 变化时可清除 owner。它影响后续谁能继续管理更新,不改变 APK 的签名校验结果。
4. 冻结与提交
4.1 冻结旧包
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
final PackageFreezer freezer = freezePackageForInstall(
packageName, UserHandle.USER_ALL,
installRequest.getInstallFlags(),
"installPackageLI",
ApplicationExitInfo.REASON_PACKAGE_UPDATED,
installRequest);
installRequest.setFreezer(freezer);冻结发生在 reconcile 之后、commit 之前。它保护旧进程不在新旧 code path 切换期间继续启动;InstallRequest 持有 freezer,后续成功或失败都由收尾逻辑关闭。
4.2 替换与注册
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
if (installRequest.isInstallReplace()) {
final AndroidPackage oldPackage = mPm.mPackages.get(packageName);
mDeletePackageHelper.executeDeletePackage(
reconciledPkg.mDeletePackageAction, packageName,
true, allUsers, false,
installRequest.isKeepArtProfile());
}
final AndroidPackage pkg = commitReconciledScanResultLocked(
reconciledPkg, allUsers);
updateSettingsLI(pkg, allUsers, installRequest);普通 APK replace 先通过 DeletePackageHelper 移除旧运行对象和旧设置关联,再把 reconcile 后的新扫描结果写入 mPackages、Settings、Resolver 和 PermissionManager。系统包更新在这里走 disabled system 分支,不能把两者的删除语义混为一谈。
4.3 提交后状态
源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
if (ps != null) {
installRequest.setNewUsers(ps.queryInstalledUsers(allUsers, true));
ps.setUpdateAvailable(false);
}
if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
mPm.updateInstantAppInstallerLocked(packageName);
}新包提交后,request 记录实际 installed users,Settings 更新 sequence number,后续广播和回调使用这些用户集合区分 update 与 first install。更新成功不代表 dexopt、app data 或广播已经全部完成,它们属于 post-commit 阶段。
5. 失败与恢复
5.1 前置失败
签名、版本、shared UID、权限声明或 shared library 检查失败时,流程不会进入 commitPackagesLocked。staging code path 和预先创建的 App ID 由失败收尾清理,旧包和用户数据保持原状。
5.2 commit 后异常
Android 17 源码在 commitReconciledScanResultLocked 注释中明确提醒:commit 中仍可能发生不可预测的 I/O 异常,导致中间状态不一致。因此“原子更新”是设计目标而非所有外部副作用的绝对事务;排查 commit 异常要检查 Settings、Resolver、App ID 和 code path 是否已经部分改变。
5.3 进程与依赖消费者
更新共享库 host 时,源码会杀死依赖客户端(除特定 static library 新版本场景)。普通应用更新则由 freezer/ActivityManager 协作停止旧进程。更新完成后,Resolver、PermissionManager、AppsFilter 和 PackageInstaller 广播分别消费新状态;任一消费者缓存未刷新都可能表现为“版本已更新但仍解析旧组件”。
6. 验证方法
6.1 正常更新
用同一签名、递增 versionCode 的 v1/v2 APK 更新,检查 pm path、dumpsys package 的 version、user 状态和 installer 信息。关键断言是 code path 切换到新版本、用户数据仍可访问、旧进程被停止或重启;这不证明所有 runtime permission 都自动变化。
6.2 签名失败
使用不同签名但同包名的 APK,预期 INSTALL_FAILED_UPDATE_INCOMPATIBLE,且 mPackages 仍指向旧 APK。若 shared UID 场景使用不兼容 lineage,预期 INSTALL_FAILED_SHARED_USER_INCOMPATIBLE。
6.3 并发版本
设置 requiredInstalledVersionCode 为旧版本,同时让另一个更新先完成,再提交当前 session。预期返回 INSTALL_FAILED_WRONG_INSTALLED_VERSION,证明 required version 是并发保护条件,不是普通 versionCode 降级检查。
adb shell pm path <package.name>
adb shell dumpsys package <package.name> | grep -E 'versionCode|codePath|installerPackageName'7. 源码路线
InstallPackageHelper.preparePackage:替换对象和旧设置来源。PackageManagerServiceUtils.verifySignatures:签名 capability、兼容和 shared UID。verifyReplacingVersionCode:required version 与降级边界。commitPackagesLocked:旧包删除和新包提交顺序。commitReconciledScanResultLocked:update owner、冻结检查和消费者注册。deleteInstalledPackageLIF/RemovePackageHelper:旧状态、数据和 code path 的处理。
普通应用更新是一条“先证明可替换,再冻结并提交”的路径:Settings 提供旧包事实,签名 lineage 保护数据接管,版本检查防止意外倒退,DeletePackageHelper 清理旧运行对象,InstallPackageHelper 最终把新对象注册给所有消费者。理解这些 owner 和屏障,才能区分更新失败、状态提交异常与应用自身数据兼容问题。
