Android随笔-APP安装那些事儿
Android 安装应用经历了6 个完整阶段从用户点击安装到系统广播通知。一、整体流程概览用户点击安装│├── Stage 1: PackageInstaller 创建 Session流式写入 APK│├── Stage 2: PMS 拷贝 APK、解析 AndroidManifest、验证签名│├── Stage 3: 权限自动授予Normal、签名/SharedUserId 校验│├── Stage 4: dex2oat AOT 编译、Native 库提取│├── Stage 5: 持久化 packages.xml、更新组件索引│└── Stage 6: 发送广播通知系统、回调应用商店二、Stage 1: 发起安装PackageInstaller2.1 安装入口安装方式入口类应用商店安装PackageInstallerActivity/InstallStagingadb installpm命令 →PackageManagerShellCommand文件管理器打开 APKPackageInstallerActivity系统 OTARecoverySystem/ApexManager2.2 Session 机制Android 7// PackageInstallerService.javapublicintcreateSession(SessionParamsparams,StringinstallerPackageName,intuserId){// 1. 验证参数validateInstallPackageName(params.appPackageName);// 2. 分配 Session IDintsessionIdallocateSessionIdLocked();// 3. 创建临时目录FilestageDirbuildSessionDir(sessionId);// /data/app/vmdl{sessionId}.tmp/// 4. 写入 session 元数据mSessions.put(sessionId,newPackageInstallerSession(...));// 5. 持久化到 install_sessions.xmlwriteSessionsLLocked();returnsessionId;}Session 的优势支持流式写入大 APK 可分片传输原子提交commit 前所有操作可回滚支持Split APKbase config 多 APK2.3 写入 APK 数据// 应用商店通过 IPackageInstallerSession.write()publicvoidwrite(Stringname,longoffsetBytes,longlengthBytes,ParcelFileDescriptorfd){// 将 APK 数据写入 session 临时文件// /data/app/vmdl{sessionId}.tmp/{name}}2.4 提交安装// 用户确认后调用 commit()publicvoidcommit(IntentSenderstatusReceiver){// 1. 验证 session 完整性// 2. 调用 PMS.installPackage() 进入实际安装mPm.installPackage(sessionStageDir,...);}三、Stage 2: 拷贝与解析PMS3.1 进入PMS// PackageManagerService.javapublicvoidinstallPackage(StringoriginPath,IPackageInstallObserver2observer,intinstallFlags,StringinstallerPackageName){// 创建 Message 发送到 Handler 线程异步处理MessagemsgmHandler.obtainMessage(INIT_COPY);msg.objnewInstallParams(originPath,observer,installFlags,...);mHandler.sendMessage(msg);}3.2 实际安装逻辑// installPackageLI 是核心方法synchronizedprivatevoidinstallPackageLI(InstallArgsargs,PackageInstalledInfores){// 1. 拷贝 APK intretargs.copyApk();// 从 /data/app/vmdl{sessionId}.tmp/ 拷贝到 /data/app/{pkg}-1/// 2. 解析 APK PackageParserppnewPackageParser();PackageParser.Packagepkgpp.parsePackage(tmpPackageFile,parseFlags);// 3. 收集签名 PackageParser.collectCertificates(pkg,parseFlags);// 4. 如果是更新验证签名一致 if(oldPkg!null){if(!compareSignatures(oldPkg.mSignatures,pkg.mSignatures)){res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,signatures mismatch);return;}}// 5. 扫描并整合到系统 scanPackageLI(pkg,...);}3.3 APK 解析细节// PackageParser.parsePackage()publicPackageparsePackage(FileapkFile,intflags){// 1. 解析 AndroidManifest.xml二进制 XMLXmlResourceParserparserapkFile.getAssets().openXmlResourceParser(AndroidManifest.xml);// 2. 解析 manifest 根元素pkg.packageNameparsePackageName(parser);pkg.mVersionCodeparseVersionCode(parser);// 3. 解析 applicationpkg.applicationInfoparseApplicationInfo(parser);// 4. 解析四大组件while(parser.next()!END_DOCUMENT){Stringtagparser.getName();switch(tag){caseactivity:pkg.activities.add(parseActivity(parser));break;caseservice:pkg.services.add(parseService(parser));break;casereceiver:pkg.receivers.add(parseActivity(parser));break;// receiver 继承 Activitycaseprovider:pkg.providers.add(parseProvider(parser));break;casepermission:pkg.permissions.add(parsePermission(parser));break;caseuses-permission:pkg.requestedPermissions.add(parsePermissionName(parser));break;}}// 5. 解析 intent-filter// 6. 解析 uses-feature, uses-library 等}四、Stage 3: 验证与权限处理4.1 签名验证// collectCertificates() 验证 APK 签名完整性publicstaticvoidcollectCertificates(Packagepkg,intparseFlags){// 1. 读取 APK 中的签名块Certificate[][]certificatesloadCertificates(apkFile);// 2. 验证每个文件的签名for(Entryentry:apkFile.entries()){verifySignature(entry,certificates);}// 3. 存储签名到 Packagepkg.mSignaturesconvertToSignatures(certificates);}4.2 权限自动授予// grantPermissionsLPw()privatevoidgrantPermissionsLPw(PackageParser.Packagepkg,booleanreplace,...){PackageSettingps(PackageSetting)pkg.mExtras;PermissionsStatepermissionsStateps.getPermissionsState();// 遍历所有申请的权限for(inti0;ipkg.requestedPermissions.size();i){Stringpermpkg.requestedPermissions.get(i);BasePermissionbpmSettings.mPermissions.get(perm);if(bpnull)continue;if(bp.isNormal()){// Normal 权限安装时自动授予permissionsState.grantInstallPermission(bp);}elseif(bp.isDangerous()){// Dangerous 权限Android 6 运行时申请安装时不授予// 记录为需要运行时申请}elseif(bp.isSignature()){// Signature 权限仅同签名应用可获取if(compareSignatures(pkg.mSignatures,bp.sourcePackage.mSignatures)SIGNATURE_MATCH){permissionsState.grantInstallPermission(bp);}}}// 写入持久化mSettings.writeLPr();}4.3 SharedUserId 验证// 如果应用声明了 sharedUserIdif(pkg.mSharedUserId!null){SharedUserSettingsharedUsermSettings.getSharedUserLPw(pkg.mSharedUserId,...);// 验证所有共享同一 UID 的应用必须签名一致if(sharedUser.signatures.mSignatures!null){if(!compareSignatures(pkg.mSignatures,sharedUser.signatures.mSignatures)){thrownewPackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE);}}// 分配或复用 UIDpkg.applicationInfo.uidsharedUser.userId;}五、Stage 4: DexOpt 与 Native 库5.1 AOT 编译dex2oat// PackageDexOptimizer.performDexOpt()intperformDexOpt(PackageParser.Packagepkg,...){// 构建 dex2oat 命令String[]dex2oatArgs{/system/bin/dex2oat,--dex-fileapkPath,--oat-fileoatPath,// /data/app/{pkg}-1/oat/arm64/base.odex--instruction-setarm64,--compiler-filterquicken// 或 speed-profile};// 执行编译intretdex2oat(dex2oatArgs);return(ret0)?DEX_OPT_PERFORMED:DEX_OPT_FAILED;}编译模式|模式|说明|适用场景||---------------|-----------------|-------------||verify|仅验证DEX不编译|首次启动快速安装||quicken|验证部分优化|大多数应用||speed-profile|基于使用 profile 编译热点|有 profile 的应用||speed|全量AOT编译|系统应用|5.2 Native 库提取// NativeLibraryHelper.copyNativeBinaries()publicstaticintcopyNativeBinaries(Packagepkg,FilenativeLibraryRoot){// 从 APK 的 lib/arm64/ 提取 .so 文件// 到 /data/app/{pkg}-1/lib/arm64/for(Stringabi:abis){ZipEntrylibEntryapkFile.getEntry(lib/abi/);extractEntry(libEntry,nativeLibraryRoot);}// 设置 SELinux 标签SELinux.restoreconRecursive(nativeLibraryRoot);}六、Stage 5: 持久化与系统更新6.1 写入 packages.xml// Settings.writeLPr()voidwriteLPr(){// 原子写入先写 .xml再 rename 覆盖原文件FiletempFilenewFile(mSettingsFilename.tmp);FileOutputStreamfstrnewFileOutputStream(tempFile);// 写入 XML 头XmlSerializerserializerXml.newSerializer();serializer.setOutput(fstr,utf-8);serializer.startDocument(null,true);// 写入所有包信息for(PackageSettingpkg:mPackages.values()){serializer.startTag(null,package);serializer.attribute(null,name,pkg.name);serializer.attribute(null,codePath,pkg.codePathString);serializer.attribute(null,version,String.valueOf(pkg.versionCode));serializer.attribute(null,userId,String.valueOf(pkg.appId));// 写入签名serializer.startTag(null,sigs);for(Signaturesig:pkg.signatures.mSignatures){serializer.startTag(null,cert);serializer.attribute(null,index,String.valueOf(sig.hashCode()));serializer.endTag(null,cert);}serializer.endTag(null,sigs);// 写入权限serializer.startTag(null,perms);for(Stringperm:pkg.grantedPermissions){serializer.startTag(null,item);serializer.attribute(null,name,perm);serializer.attribute(null,granted,true);serializer.endTag(null,item);}serializer.endTag(null,perms);serializer.endTag(null,package);}serializer.endDocument();fstr.flush();// 原子替换FileUtils.rename(tempFile,mSettingsFilename);}6.2 更新组件索引// ComponentResolver.addComponent()voidaddComponent(PackageParser.Packagepkg){// 更新 Activity 索引for(Activityactivity:pkg.activities){mActivities.addActivity(activity,activity);// 解析 intent-filter加入索引for(IntentFilterintentFilter:activity.intents){mActivities.mIntentFilterResolver.addFilter(intentFilter);}}// 同理更新 Service、Receiver、Provider 索引// 这样 AMS 查询 Intent 时才能快速匹配}七、Stage 6: 广播与回调7.1 发送系统广播// 安装完成后的广播voidsendPackageBroadcast(Stringaction,StringpackageName,...){IntentintentnewIntent(action);intent.setData(Uri.parse(package:packageName));// ACTION_PACKAGE_ADDED: 新安装// ACTION_PACKAGE_REPLACED: 覆盖更新// ACTION_PACKAGE_REMOVED: 卸载mAm.broadcastIntentInPackage(...,intent,...);}接收广播的典型组件Launcher更新桌面图标Settings更新应用列表Google Play Services更新应用状态JobScheduler重新调度任务7.2 回调应用商店// 通过 IntentSender 回调安装结果if(statusReceiver!null){IntentresultnewIntent();result.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME,packageName);result.putExtra(PackageInstaller.EXTRA_STATUS,PackageInstaller.STATUS_SUCCESS);try{statusReceiver.sendIntent(context,0,result,null,null);}catch(IntentSender.SendIntentExceptione){// 回调失败}}八、关键文件路径总结路径说明/data/app/vmdl{sessionId}.tmp/Session 临时目录commit 前/data/app/{packageName}-1/最终 APK 目录/data/app/{packageName}-1/oat/arm64/base.odexAOT 编译产物/data/app/{packageName}-1/lib/arm64/Native 库提取目录/data/data/{packageName}/应用私有数据首次启动时创建/data/system/packages.xml包数据库持久化文件/data/system/packages.list包名到 UID 的映射/data/system/install_sessions.xml活跃安装 Session九、安装失败常见原因错误码原因排查INSTALL_FAILED_INVALID_APKAPK 文件损坏或格式错误检查 APK 签名、ZIP 结构INSTALL_FAILED_UPDATE_INCOMPATIBLE签名与已安装版本不一致确认 debug/release 签名INSTALL_FAILED_SHARED_USER_INCOMPATIBLEsharedUserId 签名不匹配检查 AndroidManifestINSTALL_FAILED_INSUFFICIENT_STORAGE存储空间不足清理空间INSTALL_FAILED_DUPLICATE_PACKAGE包名冲突检查是否已安装同名包INSTALL_FAILED_NO_MATCHING_ABIS无匹配的 CPU 架构检查 APK 是否包含目标 ABI 的 soINSTALL_FAILED_VERSION_DOWNGRADE版本号低于已安装版本提升 versionCode