本文首发于微信公众号「后厂技术官」
关联系列Android包管理机制系列
前言 在上一篇文章Android包管理机制(二)PackageInstaller安装APK 中,我们学习了PackageInstaller是如何安装APK的,最后会将APK的信息交由PMS处理。那么PMS是如何处理的呢?这篇文章会给你答案。
1.PackageHandler处理安装消息 APK的信息交由PMS后,PMS通过向PackageHandler发送消息来驱动APK的复制和安装工作。 先来查看PackageHandler处理安装消息的调用时序图。
接着上一篇文章的代码逻辑来查看PMS的installStage方法。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage (String packageName, File stagedDir, String stagedCid, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { ... 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; ... mHandler.sendMessage(msg); }
注释2处创建InstallParams,它对应于包的安装数据。注释1处创建了类型为INIT_COPY的消息,在注释3处将InstallParams通过消息发送出去。
1.1 对INIT_COPY的消息的处理 处理INIT_COPY类型的消息的代码如下所示。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler
void doHandleMessage (Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); if (!mBound) { Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS" , System.identityHashCode(mHandler)); if (!connectToService()) { Slog.e(TAG, "Failed to bind to media container service" ); params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS" , System.identityHashCode(mHandler)); if (params.traceMethod != null ) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } return ; } else { mPendingInstalls.add(idx, params); } } else { mPendingInstalls.add(idx, params); if (idx == 0 ) { mHandler.sendEmptyMessage(MCS_BOUND); } } break ; } .... } } }
PackageHandler继承自Handler,它被定义在PMS中,doHandleMessage方法用于处理各个类型的消息,来查看对INIT_COPY类型消息的处理。注释1处的mBound用于标识是否绑定了DefaultContainerService,默认值为false。DefaultContainerService是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此DefaultContainerService没有和PMS运行在同一进程中,它运行在com.android.defcontainer进程,通过IMediaContainerService和PMS进行IPC通信,如下图所示。
注释2处的connectToService方法用来绑定DefaultContainerService,注释3处发送MCS_BOUND类型的消息,触发处理第一个安装请求。 查看注释2处的connectToService方法: **frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler **
private boolean connectToService () { if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + " DefaultContainerService" ); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true ; return true ; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false ; }
注释2处如果绑定DefaultContainerService成功,mBound会置为ture 。注释1处的bindServiceAsUser方法会传入mDefContainerConn,bindServiceAsUser方法的处理逻辑和我们调用bindService是类似的,服务建立连接后,会调用onServiceConnected方法: **frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java **
class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected (ComponentName name, IBinder service) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected" ); final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, Object)); } public void onServiceDisconnected (ComponentName name) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected" ); } }
注释1处发送了MCS_BOUND类型的消息,与PackageHandler.doHandleMessage
方法的注释3处不同的是,这里发送消息带了Object类型的参数,这里会对这两种情况来进行讲解,一种是消息不带Object类型的参数,一种是消息带Object类型的参数。
1.2 对MCS_BOUND类型的消息的处理 消息不带Object类型的参数 查看对MCS_BOUND类型消息的处理:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound" ); if (msg.obj != null ) { mContainerService = (IMediaContainerService) msg.obj; Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS" , System.identityHashCode(mHandler)); } if (mContainerService == null ) { if (!mBound) { Slog.e(TAG, "Cannot bind to media container service" ); for (HandlerParams params : mPendingInstalls) { params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall" , System.identityHashCode(params)); if (params.traceMethod != null ) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } return ; } mPendingInstalls.clear(); } else { Slog.w(TAG, "Waiting to connect to media container service" ); } } else if (mPendingInstalls.size() > 0 ) { ... else { Slog.w(TAG, "Empty queue" ); } break ; }
如果消息不带Object类型的参数,就无法满足注释1处的条件,注释2处的IMediaContainerService类型的mContainerService也无法被赋值,这样就满足了注释3处的条件。 如果满足注释4处的条件,说明还没有绑定服务,而此前已经在PackageHandler.doHandleMessage
方法的注释2处调用绑定服务的方法了,这显然是不正常的,因此在注释5处负责处理服务发生错误的情况。如果不满足注释4处的条件,说明已经绑定服务了,就会打印出系统log,告知用户等待系统绑定服务。
消息带Object类型的参数 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound" ); if (msg.obj != null ) { ... } if (mContainerService == null ) { ... } else if (mPendingInstalls.size() > 0 ) { HandlerParams params = mPendingInstalls.get(0 ); if (params != null ) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall" , System.identityHashCode(params)); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy" ); if (params.startCopy()) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind..." ); if (mPendingInstalls.size() > 0 ) { mPendingInstalls.remove(0 ); } if (mPendingInstalls.size() == 0 ) { if (mBound) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND" ); removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000 ); } } else { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work" ); mHandler.sendEmptyMessage(MCS_BOUND); } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); }else { Slog.w(TAG, "Empty queue" ); } break ; }
如果MCS_BOUND类型消息带Object类型的参数就不会满足注释1处的条件,就会调用注释2处的判断,如果安装请求数不大于0就会打印出注释6处的log,说明安装请求队列是空的。安装完一个APK后,就会在注释5处发出MSC_BOUND消息,继续处理剩下的安装请求直到安装请求队列为空。 注释3处得到安装请求队列第一个请求HandlerParams ,如果HandlerParams 不为null就会调用注释4处的HandlerParams的startCopy方法,用于开始复制APK的流程。
2.复制APK 先来查看复制APK的时序图。
HandlerParams是PMS中的抽象类,它的实现类为PMS的内部类InstallParams。HandlerParams的startCopy方法如下所示。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#HandlerParams
final boolean startCopy () { boolean res; try { if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this ); if (++mRetries > MAX_RETRIES) { Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up" ); mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false ; } else { handleStartCopy(); res = true ; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT" ); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false ; } handleReturnCode(); return res; }
注释1处的mRetries用于记录startCopy方法调用的次数,调用startCopy方法时会先自动加1,如果次数大于4次就放弃这个安装请求:在注释2处发送MCS_GIVE_UP类型消息,将第一个安装请求(本次安装请求)从安装请求队列mPendingInstalls中移除掉。注释4处用于处理复制APK后的安装APK逻辑,第3小节中会再次提到它。注释3处调用了抽象方法handleStartCopy,它的实现在InstallParams中,如下所示。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#InstallParams
public void handleStartCopy () throws RemoteException { ... final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0 ; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0 ; final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 ; PackageInfoLite pkgLite = null ; if (onInt && onSd) { Slog.w(TAG, "Conflicting flags specified for installing on both internal and external" ); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (onSd && ephemeral) { Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external" ); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); if (DEBUG_EPHEMERAL && ephemeral) { Slog.v(TAG, "pkgLite for install: " + pkgLite); } ... if (ret == PackageManager.INSTALL_SUCCEEDED) { int loc = pkgLite.recommendedInstallLocation; if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) { ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) { ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; } ... }else { loc = installLocationPolicy(pkgLite); ... } } final InstallArgs args = createInstallArgs(this ); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { ... if (!origin.existing && requiredUid != -1 && isVerificationEnabled( verifierUser.getIdentifier(), installFlags, installerUid)) { ... } else { ret = args.copyApk(mContainerService, true ); } } mRet = ret; }
handleStartCopy方法的代码很多,这里截取关键的部分。 注释1处通过IMediaContainerService跨进程调用DefaultContainerService的getMinimalPackageInfo方法,该方法轻量解析APK并得到APK的少量信息,轻量解析的原因是这里不需要得到APK的全部信息,APK的少量信息会封装到PackageInfoLite中。接着在注释2处确定APK的安装位置。注释3处创建了InstallArgs,InstallArgs 是一个抽象类,定义了APK的安装逻辑,比如复制和重命名APK等,它有3个子类,都被定义在PMS中,如下图所示。 其中FileInstallArgs用于处理安装到非ASEC的存储空间的APK,也就是内部存储空间(Data分区),AsecInstallArgs用于处理安装到ASEC中(mnt/asec)即SD卡中的APK。MoveInstallArgs用于处理已安装APK的移动的逻辑。 对APK进行检查后就会在注释4处调用InstallArgs的copyApk方法进行安装。 不同的InstallArgs子类会有着不同的处理,这里以FileInstallArgs为例。FileInstallArgs的copyApk方法中会直接return FileInstallArgs的doCopyApk方法:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#FileInstallArgs
private int doCopyApk (IMediaContainerService imcs, boolean temp) throws RemoteException { ... try { final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 ; final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); codeFile = tempDir; resourceFile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } ... int ret = PackageManager.INSTALL_SUCCEEDED; ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); ... return ret; }
注释1处用于创建临时存储目录,比如/data/app/vmdl18300388.tmp,其中18300388是安装的sessionId。注释2处通过IMediaContainerService跨进程调用DefaultContainerService的copyPackage方法,这个方法会在DefaultContainerService所在的进程中将APK复制到临时存储目录,比如/data/app/vmdl18300388.tmp/base.apk。目前为止APK的复制工作就完成了,接着就是APK的安装过程了。
3.安装APK 照例先来查看安装APK的时序图。 我们回到APK的复制调用链的头部方法:HandlerParams的startCopy方法,在注释4处会调用handleReturnCode方法,它的实现在InstallParams中,如下所示。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void handleReturnCode () { if (mArgs != null ) { processPendingInstall(mArgs, mRet); } } private void processPendingInstall (final InstallArgs args, final int currentStatus) { mHandler.post(new Runnable() { public void run () { mHandler.removeCallbacks(this ); 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); } ... } }); }
handleReturnCode方法中只调用了processPendingInstall方法,注释1处用于检查APK的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的APK文件,注释3处用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件。主要来看注释2处的installPackageTracedLI方法,其内部会调用PMS的installPackageLI方法。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installPackageLI (InstallArgs args, PackageInstalledInfo res) { ... 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); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI" , e); return ; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } ... pp = null ; String oldCodePath = null ; boolean systemApp = false ; synchronized (mPackages) { if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0 ) { String oldName = mSettings.getRenamedPackageLPr(pkgName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true ; if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" + oldName + " pkgName=" + pkgName); } ... } PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null ) { if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); PackageSetting signatureCheckPs = ps; if (pkg.applicationInfo.isStaticSharedLibrary()) { SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg); if (libraryEntry != null ) { signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk); } } 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 ; } } ... } int N = pkg.permissions.size(); for (int i = N-1 ; i >= 0 ; i--) { PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); } } } if (systemApp) { if (onExternal) { res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard" ); return ; } else if (instantApp) { res.setError(INSTALL_FAILED_INSTANT_APP_INVALID, "Cannot update a system app with an instant app" ); return ; } } ... if (!args.doRename(res.returnCode, pkg, oldCodePath)) { res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename" ); return ; } startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI" )) { if (replace) { ... 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); } } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null ) { res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true ); ps.setUpdateAvailable(false ); } ... } }
installPackageLI方法的代码有将近500行,这里截取主要的部分,主要做了几件事:
创建PackageParser解析APK。
检查APK是否存在,如果存在就获取此前没被改名前的包名并在注释1处赋值给PackageParser.Package类型的pkg,在注释3处将标志位replace置为true表示是替换安装。
注释3处,如果Settings中保存有要安装的APK的信息,说明此前安装过该APK,则需要校验APK的签名信息,确保安全的进行替换。
在注释4处将临时文件重新命名,比如前面提到的/data/app/vmdl18300388.tmp/base.apk,重命名为/data/app/包名-1/base.apk。这个新命名的包名会带上一个数字后缀1,每次升级一个已有的App,这个数字会不断的累加。
系统APP的更新安装会有两个限制,一个是系统APP不能在SD卡上替换安装,另一个是系统APP不能被Instant App替换。
注释5处根据replace来做区分,如果是替换安装就会调用replacePackageLIF方法,其方法内部还会对系统APP和非系统APP进行区分处理,如果是新安装APK会调用installNewPackageLIF方法。
这里我们以新安装APK为例,会调用PMS的installNewPackageLIF方法。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installNewPackageLIF (PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res, int installReason) { ... 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 { 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); }
installNewPackageLIF主要做了以下3件事:
扫描APK,将APK的信息存储在PackageParser.Package类型的newPackage中,一个Package的信息包含了1个base APK以及0个或者多个split APK。
更新该APK对应的Settings信息,Settings用于保存所有包的动态设置。
如果安装成功就为新安装的应用程序准备数据,安装失败就删除APK。
安装APK的过程就讲到这里,就不再往下分析下去,有兴趣的同学可以接着深挖。
**4.总结 ** 本文主要讲解了PMS是如何处理APK安装的,主要有几个步骤:
PackageInstaller安装APK时会将APK的信息交由PMS处理,PMS通过向PackageHandler发送消息来驱动APK的复制和安装工作。
PMS发送INIT_COPY和MCS_BOUND类型的消息,控制PackageHandler来绑定DefaultContainerService,完成复制APK等工作。
复制APK完成后,会开始进行安装APK的流程,包括安装前的检查、安装APK和安装后的收尾工作。
参考资料Android包管理机制 Android app安装过程分析(基于Nougat) 应用程序安装流程