Android中应用安装分析

少年乘勇气,百战过乌孙。这篇文章主要讲述Android中应用安装分析相关的知识,希望能为你提供帮助。
#1 安装方式

  • 1 安装系统APK和预制APK时,通过PMS的构造函数中安装,即第一次开机时安装应用,没有安装界面。
  • 2 网络下载安装,通过应用商店等,即调用PackageManager.installPackages(),有安装界面。
  • 3 通过adb工具安装,没有安装界面,它通过启动pm脚本的形式,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装。
  • 4 安装本地apk,有安装界面,由PackageInstaller系统应用安装。
    上述几种方式均通过PackageInstallObserver来监听安装是否成功。
#2 安装流程分析 2.1 首次安装
首次安装即系统第一次开机时安装应用,包括系统应用和预制应用,其最主要过程在PMS构造函数中,
整个过程关键步骤大致为上述15步,与应用安装相关实际上就是扫描和安装两步。方法调用时序图如图1所示。
[图1 PMS安装应用时序图]
Android中应用安装分析

文章图片

  • 1 向动态设置中添加系统默认的共享ID(system、phone、log、nfc、bluetooth、shell、se等)。
  • 2 初始化成员变量,如Installer、PackageDexOptimizer、DexManager、ArtManagerService、MoveCallbacks、OnPermissionChangeListeners等,并获取系统配置。
  • 3 启动一个服务类线程。
  • 4 初始化用户管理服务
  • 5 将权限配置传入包管理器
  • 6 清除代码路径不存在的孤立包
  • 7 将系统应用权限从安装态升级为运行时
  • 8 在扫描应用前,手机供应商的覆盖安装包(/overlay)
  • 9 扫描应用目录,依次为特权系统目录 priv-app、普通目录 app、供应商系统目录 vendor/app等
  • 10 解析存储管理器
  • 11 如果是第一次开机,需要初始化用户的默认偏好应用
  • 12 在启动时,为用户准备好存储空间,因为SystemUI等启动不能等待用户
  • 13 安装应用,完成后检查webview,默认浏览器等。
  • 14 启动PackageInstallerService
  • 15 向系统组件暴露私有服务
    下面我们结合代码做详细分析
  1. 判断应用包是否已安装,如果包名存在于uninstalled_deapp.xml中或者已安装,则直接返回null。
2.2 下载安装
下载安装可分为两部分:拷贝应用和安装应用。拷贝过程的函数调用时序图如图2所示。
【图2 下载安装应用程序时序图】
Android中应用安装分析

文章图片

frameworks层的入口函数为PackageManager.installPackage,由应用市场APP调用,然后调用PMS.installPackageAsUser,然后发送消息INIT_COPY、MCS_BOUND开始复制,调用HandlerParams.startCopy。这个方法主要分两部分,一部分是拷贝应用的执行程序,另一部分是创建应用的数据目录,拷贝部分由handleStartCopy完成。之后调用handlerReturnCode来处理创建数据目录。拷贝部分会调用DefaultContainerService来完成,该服务为那些可能位于可删除空间上的文件提供检查和拷贝功能。当底层设置被移除时,这样设计可以防止系统进程保留打开的文件时,不被内核杀死。
handleStartcopy实现在PMS内部类InstallParams中,它的功能是调用远程方法获取包信息和安装位置,如有必要则给与默认车辆覆盖安装位置,然后基于安装位置创建安装参数。下面我们结合关键代码做进一步分析。
首先是拷贝应用过程
  • 1 PMS.installPackageAsUser的功能主要是:根据uid确定installFlags,并校验权限,并构造InstallParam,然后发送INIT_COPY消息。
@Override public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser"); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { try { if (observer != null) { observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null); } } catch (RemoteException re) { } return; }if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { installFlags |= PackageManager.INSTALL_FROM_ADB; } else { // Caller holds INSTALL_PACKAGES permission, so we\'re less strict // about installerPackageName.installFlags & = ~PackageManager.INSTALL_FROM_ADB; installFlags & = ~PackageManager.INSTALL_ALL_USERS; }UserHandle user; if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(userId); }// Only system components can circumvent runtime permissions when installing. if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 & & mContext.checkCallingOrSelfPermission(Manifest.permission .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { throw new SecurityException("You need the " + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); }if ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0 || (installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { throw new IllegalArgumentException( "New installs into ASEC containers no longer supported"); }final File originFile = new File(originPath); final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); final Message msg = mHandler.obtainMessage(INIT_COPY); final VerificationInfo verificationInfo = new VerificationInfo( null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid); final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer, installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user, null /*packageAbiOverride*/, null /*grantedPermissions*/, null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN); params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params)); msg.obj = params; Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installAsUser", System.identityHashCode(msg.obj)); Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(msg.obj)); mHandler.sendMessage(msg); }

  • 2 之后根据Handler.doHandleMessage调用到InstallParams.handleStartCopy方法,首先检查文件和cid是否已生成,如生成则设置installFlags。
// [InstallParams.handleStartCopy] if (origin.staged) { if (origin.file != null) { installFlags |= PackageManager.INSTALL_INTERNAL; installFlags & = ~PackageManager.INSTALL_EXTERNAL; } else if (origin.cid != null) { installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags & = ~PackageManager.INSTALL_INTERNAL; } else { throw new IllegalStateException("Invalid stage location"); } }

  • 3 然后检查空间大小,如果空间不够则释放无用空间。
// [InstallParams.handleStartCopy] if (!origin.staged & & pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { // TODO: focus freeing disk space on the target device final StorageManager storage = StorageManager.from(mContext); final long lowThreshold = storage.getStorageLowBytes( Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( origin.resolvedPath, isForwardLocked(), packageAbiOverride); try { mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); } catch (InstallerException e) { Slog.w(TAG, "Failed to free cache", e); }if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { pkgLite.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } }

  • 4 覆盖原有安装位置的文件,并根据返回结果来确定函数的返回值,并设置installFlags。
// [InstallParams.handleStartCopy] // Override with defaults if needed. loc = installLocationPolicy(pkgLite); if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) { ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; } else if (!onSd & & !onInt) { // Override install location with flags if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { // Set the flag to install on external media. installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags & = ~PackageManager.INSTALL_INTERNAL; } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag"); } installFlags |= PackageManager.INSTALL_INSTANT_APP; installFlags & = ~(PackageManager.INSTALL_EXTERNAL |PackageManager.INSTALL_INTERNAL); } else { // Make sure the flag for installing on external // media is unset installFlags |= PackageManager.INSTALL_INTERNAL; installFlags & = ~PackageManager.INSTALL_EXTERNAL; } }

  • 5 确定是否有任何已安装的包验证器,如有,则延迟检测。主要分三步:首先新建一个验证Intent,然后设置相关的信息,之后获取验证器列表,最后向每个验证器发送验证Intent。
// [InstallParams.handleStartCopy] final Intent verification = new Intent( //构造验证Intent Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); // ......final PackageVerificationState verificationState = new PackageVerificationState( requiredUid, args); mPendingVerification.append(verificationId, verificationState); // 获取验证器列表 final List< ComponentName> sufficientVerifiers = matchVerifiers(pkgLite, receivers, verificationState); DeviceIdleController.LocalService idleController = getDeviceIdleController(); final long idleDuration = getVerificationTimeout(); /* * If any sufficient verifiers were listed in the package * manifest, attempt to ask them. */ if (sufficientVerifiers != null) { final int N = sufficientVerifiers.size(); if (N == 0) { Slog.i(TAG, "Additional verifiers required, but none installed."); ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; } else { for (int i = 0; i < N; i++) { final ComponentName verifierComponent = sufficientVerifiers.get(i); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), verifierComponent.getPackageName(), idleDuration, verifierUser.getIdentifier(), false, "package verifier"); // 向每个验证器发送验证Intent final Intent sufficientIntent = new Intent(verification); sufficientIntent.setComponent(verifierComponent); mContext.sendBroadcastAsUser(sufficientIntent, verifierUser); } } }

  • 6 向验证器客户端发送intent,只有当验证成功之后才会开启copy工作。如果没有任何验证器则直接拷贝。
下面为安装过程入口是PMS.processPendingInstall方法,调用时序图如图3
【图3 下载安装-安装过程图】
Android中应用安装分析

文章图片

  • 1 首先启动一个新线程,然后设置安装信息,处理安装参数,开始安装,并发送关于安装状态的广播,然后处理安装完的事情,比如打印错误信息,清除临时文件等。
private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); res.setReturnCode(currentStatus); res.uid = -1; res.pkg = null; res.removedInfo = null; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageTracedLI(args, res); } args.doPostInstall(res.returnCode, res.uid); //...... }

  • 2 installPackageTracedLI是安装过程的核心方法,然后调用installPackageLI.首先检查安装包的完整性并解析安装包。
//[PMS.installPackageLI] // 完整性校验 if (instantApp & & (forwardLocked || onExternal)) { Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked + " external=" + onExternal); res.setReturnCode(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID); return; }// 检索包设置,并解析应用 final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | PackageParser.PARSE_ENFORCE_CODE | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0) | (instantApp ? PackageParser.PARSE_IS_EPHEMERAL : 0) | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); final PackageParser.Package pkg; try { //解析安装包 pkg = pp.parsePackage(tmpPackageFile, parseFlags); DexMetadataHelper.validatePackageDexMetadata(pkg); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }

  • 3 检查SDK版本和沙箱版本,同时检查是否有静态共享库,如有则需要放在内部存储中。
//[PMS.installPackageLI] //检查SDK版本和沙箱版本 if (instantApp & & pkg.applicationInfo.targetSdkVersion < = Build.VERSION_CODES.N_MR1) { Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target O"); res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE, "Instant app package must target O"); return; } if (instantApp & & pkg.applicationInfo.targetSandboxVersion != 2) { Slog.w(TAG, "Instant app package " + pkg.packageName + " does not target targetSandboxVersion 2"); res.setError(INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE, "Instant app package must use targetSanboxVersion 2"); return; } //检查是否有静态共享库 if (pkg.applicationInfo.isStaticSharedLibrary()) { // Static shared libraries have synthetic package names renameStaticSharedLibraryPackage(pkg); // No static shared libs on external storage if (onExternal) { Slog.i(TAG, "Static shared libs can only be installed on internal storage."); res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Packages declaring static-shared libs cannot be updated"); return; } }

  • 4 检查是否有子安装包,如有则子安装包也需要检测。
//[PMS.installPackageLI] // If we are installing a clustered package add results for the children if (pkg.childPackages != null) { synchronized (mPackages) { final int childCount = pkg.childPackages.size(); for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); PackageInstalledInfo childRes = new PackageInstalledInfo(); childRes.setReturnCode(PackageManager.INSTALL_SUCCEEDED); childRes.pkg = childPkg; childRes.name = childPkg.packageName; PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); if (childPs != null) { childRes.origUsers = childPs.queryInstalledUsers( sUserManager.getUserIds(), true); } if ((mPackages.containsKey(childPkg.packageName))) { childRes.removedInfo = new PackageRemovedInfo(this); childRes.removedInfo.removedPackage = childPkg.packageName; childRes.removedInfo.installerPackageName = childPs.installerPackageName; } if (res.addedChildPackages == null) { res.addedChildPackages = new ArrayMap< > (); } res.addedChildPackages.put(childPkg.packageName, childRes); } } }

  • 5 检查安装包是否已存在,如已存在则需要检查旧的父包、沙箱、sdk等是否已为空,否则会报错。
  • 6 校验安装包签名
//[PMS.installPackageLI] PackageSetting signatureCheckPs = ps; if (pkg.applicationInfo.isStaticSharedLibrary()) { SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg); if (libraryEntry != null) { signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk); } }// Quick sanity check that we\'re signed correctly if updating; // we\'ll check this again later when scanning, but we want to // bail early here before tripping over redefined permissions. if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) { if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) { res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); return; } } else { try { verifySignaturesLP(signatureCheckPs, pkg); } catch (PackageManagerException e) { res.setError(e.error, e.getMessage()); return; } }

  • 7 设置相关的全向,包括生成权限、移植权限等
  • 8 如果这是一个系统应用,则检查是否在外部存储上或是是否被其他应用替换等
//[PMS.installPackageLI] if (systemApp) { if (onExternal) { // Abort update; system app can\'t be replaced with app on sdcard res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard"); return; } else if (instantApp) { // Abort update; system app can\'t be replaced with an instant app res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, "Cannot update a system app with an instant app"); return; } }

  • 9 生成安装包Abi(Application binary interface,应用二进制接口,描述应用程序和操作系统之间或其他应用程序的低级接口)
//[PMS.installPackageLI] try { String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); derivePackageAbi(pkg, new File(pkg.codePath), abiOverride, extractNativeLibs, mAppLib32InstallDir); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); return; }

  • 10更新共享库
//[PMS.installPackageLI] synchronized (mPackages) { try { updateSharedLibrariesLPr(pkg, null); } catch (PackageManagerException e) { Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); } }

  • 11如有必要,优化dex文件
//[PMS.installPackageLI] final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED) & & !forwardLocked & & !pkg.applicationInfo.isExternalAsec() & & (!instantApp || Global.getInt(mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) & & ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0); if (performDexopt) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Do not run PackageDexOptimizer through the local performDexOpt // method because `pkg` may not be in `mPackages` yet. // // Also, don\'t fail application installs if the dexopt step fails. DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName, REASON_INSTALL, DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles, null /* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(pkg.packageName), dexoptOptions); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }

  • 12替换安装,则直接安装新包,这里应用时生成应用数据目录。ps:替换安装:其主要过程为更新设置,清除原有的某些APP数据,重新生成相关的app数据目录等步骤,同事要区分系统应用替换和非系统应用替换。而安装新包:则直接更新设置,生成APP数据即可。
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI")) { if (replace) { if (pkg.applicationInfo.isStaticSharedLibrary()) { // Static libs have a synthetic package name containing the version // and cannot be updated as an update would get a new package name, // unless this is the exact same version code which is useful for // development. PackageParser.Package existingPkg = mPackages.get(pkg.packageName); if (existingPkg != null & & existingPkg.mVersionCode != pkg.mVersionCode) { res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring " + "static-shared libs cannot be updated"); return; } } replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, installerPackageName, res, args.installReason); } else { installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res, args.installReason); } }

  • 13 如果是安装一个不存在的包,则调用PMS.installNewPackageLIF方法。首先会检查是否有重复的包名,并更新设置,然后根据安装的结果,如果安装失败则删除安装过程中产生的文件。
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res, int installReason) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage"); // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); synchronized(mPackages) { final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName); if (renamedPackage != null) { // 如果已有相同包名的应用,则报错 res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName + " without first uninstalling package running as " + renamedPackage); return; } if (mPackages.containsKey(pkgName)) { // Don\'t allow installation over an existing package with the same name. res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName + " without first uninstalling."); return; } }try { PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { prepareAppDataAfterInstallLIF(newPackage); } else { // Remove package from internal structures, but keep around any // data that might have already existed deletePackageLIF(pkgName, UserHandle.ALL, false, null, PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null); } } catch (PackageManagerException e) { res.setError("Package couldn\'t be installed in " + pkg.codePath, e); }Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }

  • 14 然后为已安装的应用准备数据目录,其依次的顺序是
    • PMS.prepareAppDataAfterInstallLIF
    • PMS.prepareAppDataLIF
    • PMS.prepareAppDataLeafLIF
    • Installer.createAppData
这个方法是PMS与Installer交互的接口函数,这里的数据目录是CE类型。
private void prepareAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { if (DEBUG_APP_DATA) { Slog.v(TAG, "prepareAppData for " + pkg.packageName + " u" + userId + " 0x" + Integer.toHexString(flags)); }final String volumeUuid = pkg.volumeUuid; final String packageName = pkg.packageName; final ApplicationInfo app = pkg.applicationInfo; final int appId = UserHandle.getAppId(app.uid); Preconditions.checkNotNull(app.seInfo); long ceDataInode = -1; try { // 调用Installd守护进程的入口 ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, appId, app.seInfo, app.targetSdkVersion); } catch (InstallerException e) { //...... } // Prepare the application profiles. mArtManagerService.prepareAppProfiles(pkg, userId); if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 & & ceDataInode != -1) { // TODO: mark this structure as dirty so we persist it! synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { ps.setCeDataInode(ceDataInode, userId); } } }prepareAppDataContentsLeafLIF(pkg, userId, flags); }

  • 15 如果是替换应用,一般情况是应用更新,或者是重新安装。它的主要过程包括:验证签名,如是系统更新则还需要校验hash值,检查共享ID的更改情况,不允许完整更新,更新已被删除数据,最后根据应用是否是系统应用来判断接下去的操作。
private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user, String installerPackageName, PackageInstalledInfo res, int installReason) { final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; final PackageParser.Package oldPackage; final PackageSetting ps; final String pkgName = pkg.packageName; final int[] allUsers; final int[] installedUsers; // ......boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { // Set the system/privileged flags as needed final boolean privileged = (oldPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; final int systemPolicyFlags = policyFlags | PackageParser.PARSE_IS_SYSTEM | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0); replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags, user, allUsers, installerPackageName, res, installReason); } else { replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags, user, allUsers, installerPackageName, res, installReason); } }

  • 16 最后这两个方法均会调用到PMS.prepareAppDataLeafLIF。
  • 17 安装完成后,更新设置,更新安装锁等。
2.3 adb安装
关于adb安装,其copy过程与下载安装不同,但安装过程却与下载过程是相同的,这里不做重复分析,需要注意的是adb安装是不能替换安装的,具体原因?
拷贝过程
其调用时序图如图4 所示。
【图4 adb安装-copy过程时序图】
Android中应用安装分析

文章图片

  • 1 adb的入口在com.android.commands.pm.Pm类,那么这是如何调用到这个类的呢,这是adb命令通过adbd守护进程调用到/system/bin/pm这个脚本,其脚本源码如下:
base=/system export CLASSPATh-$base/framework/pm.jar exec app_process $base/bin.com.android.commands.pm.Pm "$@"

  • 2 Pm类通过脚本启动,执行顺序是main-> run-> runInstall,然后提交session。
public static void main(String[] args) { int exitCode = 1; try { exitCode = new Pm().run(args); } catch (Exception e) { Log.e(TAG, "Error", e); System.err.println("Error: " + e); if (e instanceof RemoteException) { System.err.println(PM_NOT_RUNNING_ERR); } } System.exit(exitCode); } public int run(String[] args) throws RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); } mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE)); mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE)); mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (mPm == null) { System.err.println(PM_NOT_RUNNING_ERR); return 1; } mInstaller = mPm.getPackageInstaller(); mArgs = args; String op = args[0]; mNextArg = 1; //...... if ("install".equals(op)) { return runInstall(); } //...... }

  • 3 Pm.runInstall中首先是创建session,然后提交session,代码如下。
private int runInstall() throws RemoteException { long startedTime = SystemClock.elapsedRealtime(); final InstallParams params = makeInstallParams(); final String inPath = nextArg(); if (params.sessionParams.sizeBytes == -1 & & !STDIN_PATH.equals(inPath)) { File file = new File(inPath); if (file.isFile()) { try { ApkLite baseApk = PackageParser.parseApkLite(file, 0); PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, null, null); params.sessionParams.setSize( PackageHelper.calculateInstalledSize(pkgLite, false, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { System.err.println("Error: Failed to parse APK file: " + e); return 1; } } else { System.err.println("Error: Can\'t open non-file: " + inPath); return 1; } }final int sessionId = doCreateSession(params.sessionParams, params.installerPackageName, params.userId); try { if (inPath == null & & params.sessionParams.sizeBytes == -1) { System.err.println("Error: must either specify a package size or an APK file"); return 1; } if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk", false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) { return 1; } Pair< String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/); if (status.second != PackageInstaller.STATUS_SUCCESS) { return 1; } Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime() - startedTime) + " ms"); System.out.println("Success"); return 0; } finally { try { mInstaller.abandonSession(sessionId); } catch (Exception ignore) { } } }

  • 4 这里Pm相当于客户端,接受session的服务端在PackageInstallerSession中,这里利用AIDL来完成传输,其调用过程为:
    • Pm.doCommitSession
    • PackageInstaller.Session.commit
    • IPackageInstallerSession.commit
    • PackageInstallerSession.commit
    • Handler.Callback.handleMessage
    • PackageInstallerSession.commitLock
    • PMS.installStage
以上关于session传递过程暂不分析,下面我们来详细看下installStage方法。
  • 5 installStage方法主要功能就是构造InstallParam对象,并发送INIT_COPY。
void installStage(String packageName, File stagedDir, String stagedCid, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { if (DEBUG_EPHEMERAL) { if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { Slog.d(TAG, "Ephemeral install of " + packageName); } } final VerificationInfo verificationInfo = new VerificationInfo( sessionParams.originatingUri, sessionParams.referrerUri, sessionParams.originatingUid, installerUid); final OriginInfo origin; if (stagedDir != null) { origin = OriginInfo.fromStagedFile(stagedDir); } else { origin = OriginInfo.fromStagedContainer(stagedCid); }final Message msg = mHandler.obtainMessage(INIT_COPY); final int installReason = fixUpInstallReason(installerPackageName, installerUid, sessionParams.installReason); final InstallParams params = new InstallParams(origin, null, observer, sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid, verificationInfo, user, sessionParams.abiOverride, sessionParams.grantedRuntimePermissions, certificates, installReason); params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage", System.identityHashCode(msg.obj)); Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(msg.obj)); mHandler.sendMessage(msg); }

  • 6 发送完Handler消息后就与下载安装过程相同了。
2.4 本地安装
本地安装参与对象包括PackageInstaller应用,PMS两部分。下面我们就来分析下PackageInstaller是如何调用到PMS中的。函数调用时序图如图5所示。
【图5 本地安装前提调用时序图】
Android中应用安装分析

文章图片

  • 1 点击文件管理器中的apk文件时,会调用到FolderFragment类的openFile方法,然后调用startActivitySafety方法启动PackageInstallerActivity。
private void openFile(File f) { final Uri fileUri = Uri.fromFile(f); final Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_TITLE, f.getName()); intent.putExtra(EXTRA_ALL_VIDEO_FOLDER, true); Uri contentUri = null; String type = getMIMEType(f); //...... if (contentUri != null) { intent.setDataAndType(contentUri, type); } else { intent.setDataAndType(fileUri, type); } try { startActivitySafely(intent); } //...... }

  • 2 如下为PackageInstallerActivity.onCreate方法源码,其主要过程初始化各个服务的成员变量如PMS,校验session,并加载UI界面,然用户确定是否安装。
//[PackageInstallerActivity.java] protected void onCreate(Bundle icicle) { super.onCreate(icicle); if (icicle != null) { mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY); } //初始化各个关键参数 mPm = getPackageManager(); mIpm = AppGlobals.getPackageManager(); mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); mInstaller = mPm.getPackageInstaller(); mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); final Intent intent = getIntent(); mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE); mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO); mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, PackageInstaller.SessionParams.UID_UNKNOWN); mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) ? getPackageNameForUid(mOriginatingUid) : null; final Uri packageUri; //校验session if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) { final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); if (info == null || !info.sealed || info.resolvedBaseCodePath == null) { Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); finish(); return; }mSessionId = sessionId; packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath)); mOriginatingURI = null; mReferrerURI = null; } else { mSessionId = -1; packageUri = intent.getData(); mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); }// if there\'s nothing to do, quietly slip into the ether if (packageUri == null) { Log.w(TAG, "Unspecified source"); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); finish(); return; }if (DeviceUtils.isWear(this)) { showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR); return; }boolean wasSetUp = processPackageUri(packageUri); if (!wasSetUp) { return; }// 加载UI界面 bindUi(R.layout.install_confirm, false); checkIfAllowedAndInitiateInstall(); }

  • 3 当用户点击安装按钮时,响应函数为PackageInstallerActivity.onClick方法,
//[PackageInstallerActivity.java] public void onClick(View v) { if (v == mOk) { if (mOk.isEnabled()) { if (mOkCanInstall || mScrollView == null) { if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, true); finish(); } else { startInstall(); } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } } else if (v == mCancel) { // Cancel and finish setResult(RESULT_CANCELED); if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, false); } finish(); } }

  • 4 之后调用 PackageInstallerActivity.startInstall方法,构造Intent,然后启动InstallInstalling,并销毁PackageInstallerActivity。
private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this, InstallInstalling.class); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } if (mReferrerURI != null) { newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); } if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); } if (installerPackageName != null) { newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); } if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); }

  • 5 之后启动InstallInstalling,因为Activity中的默认成员方法的执行顺序是onCreate-> onStart-> onResume...其中onCreate的方法中主要过程包括:
    • 1 获取待安装应用信息
    • 2 根据应用安装与否决定如何调用方法
    • 3 如果已存在,则直接调用PackageManager.installExistingPackage
    • 4 如果不存在则构造session
    • 5 之后则为安装事件广播添加一个监测
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.install_installing); // 获取待安装应用信息 ApplicationInfo appInfo = getIntent() .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); // 如果应用已存在,则使用这条路径安装 if ("package".equals(mPackageURI.getScheme())) { try { getPackageManager().installExistingPackage(appInfo.packageName); launchSuccess(); } catch (PackageManager.NameNotFoundException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { //否则使用session提交安装应用 final File sourceFile = new File(mPackageURI.getPath()); PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, sourceFile), R.id.app_snippet); // 如果session已存在,则获取sessionId等数据 if (savedInstanceState != null) { mSessionId = savedInstanceState.getInt(SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); // Reregister for result; might instantly call back if result was delivered while // activity was destroyed try { InstallEventReceiver.addObserver(this, mInstallId, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { // Does not happen } } else { // 否则创建session PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); // ...... try { mInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } //创建session try { mSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } //...... mSessionCallback = new InstallSessionCallback(); } }

  • 6 InstallInstalling.onStart中注册回调函数,然后onResume中执行AsyncTask。
@Override protected void onResume() { super.onResume(); // This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId); //如果session非空,则执行AsyncTask if (sessionInfo != null & & !sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else { // we will receive a broadcast when the install is finished mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } }

  • 7 AsyncTask是Android提供的一种轻量级的异步类,执行过程可以表示为5个阶段。
    • 1 准备执行,onPreExecute()
    • 2 正在后台执行,doInBackgroud()
    • 3 进度更新,onProcessUpdate()
    • 4 完成后台任务,onPostExecute()
    • 5 取消任务,onCacelled()
此处重写了方法onPostExecute方法,源码如下。
@Override protected void onPostExecute(PackageInstaller.Session session) { if (session != null) { Intent broadcastIntent = new Intent(BROADCAST_ACTION); broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntent.setPackage( getPackageManager().getPermissionControllerPackageName()); broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallInstalling.this, mInstallId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); //提交session session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } else { getPackageManager().getPackageInstaller().abandonSession(mSessionId); if (!isCancelled()) { launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null); } } }

  • 8 session对象传输顺序为:
    • 1 PackageInstaller.Session.commit
    • 2 IPackageInstallerSession.commit
    • 3 PackageInstallerSession.commit
    • 4 Handler.Callback.handleMessage
    • 5 PackageInstallerSession.commitLock
    • 6 PMS.installStage
      这里是不是似曾相识,这一步跟Adb安装的第4步几乎相同,之后就调用installStage方法完成安装。
#3 总结【Android中应用安装分析】安装应用的场景就是上述所示的PMS构造函数安装、adb安装、网络下载安装、本地安装。其最终的入口为PMS.prepareAppDataLeafLIF,然后调用Installer类完成安装,这里涉及到System_server到Installd守护进程的转移。

    推荐阅读