内容简介:在本系列上一篇文章在前一篇文章末尾,我们讲过 commitLocked 方法,我们回顾下:正式进入 PMS 源码分析流程,我们看看 installStage 方法:
开篇
核心源码
关键类 | 路径 |
---|---|
PackageInstallerSession.java | frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java |
PackageManagerService.java | frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java |
前言
在本系列上一篇文章 【深入研究 PackageManagerService 系列(5)之 PackageInstaller - APK 安装流程】 中,我们了解了 PackageInstaller 安装 APK 的流程,最后会将 APK 的信息交由 PMS 处理。那么 PMS 是如何处理的?这就是我们这篇文章需要分析的。
PackageHandler
commitLocked
在前一篇文章末尾,我们讲过 commitLocked 方法,我们回顾下:
private final PackageManagerService mPm; private void commitLocked() throws PackageManagerException { ... ... /** * commitLocked 方法很长,我们主要关注这一行代码 * 调用 PackageManagerService 的 installStage 方法 * 这样安装 APK 的代码逻辑就进入了 PackageManagerService 中 */ mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, mInstallerPackageName, mInstallerUid, user, mCertificates); }
installStage
正式进入 PMS 源码分析流程,我们看看 installStage 方法:
void installStage(String packageName, File stagedDir, String stagedCid, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { ... ... // 创建类型为 INIT_COPY 的消息 final Message msg = mHandler.obtainMessage(INIT_COPY); final int installReason = fixUpInstallReason(installerPackageName, installerUid, sessionParams.installReason); // 创建 InstallParams,它对应于包的安装数据 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; ... ... // 将 InstallParams 通过消息发送出去 mHandler.sendMessage(msg); }
handleMessage
因为 PackageHandler 继承 Handler ,所以我们来看下 PackageHandler 的 HandlerMessage 方法:
public void handleMessage(Message msg) { try { doHandleMessage(msg); } finally { // 设置了线程的优先级为后台线程 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } }
INIT_COPY
接下来看下 INIT_COPY 消息的处理流程:
class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>(); ... ... // 用于处理各个类型的消息 void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { // 取出 InstallParams HandlerParams params = (HandlerParams) msg.obj; // idx 为当前需要安装的 APK 个数,mPendingInstalls 里面保存所有需要安装的 APK 解析出来的 HandlerParams 参数 int idx = mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); // mBound 用于标识是否绑定了服务(DefaultContainerService), // 如果已经绑定了,则 mBound 为true,如果是第一次调用 mBound 为 false,默认值为 false。 if (!mBound) { Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); // 如果没有绑定服务,重新绑定,connectToService 方法内部如果绑定成功会将 mBound 置为 true 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 return; } else { // 绑定服务成功,将请求添加到 ArrayList 类型的 mPendingInstalls 中,等待处理 mPendingInstalls.add(idx, params); } } else { // 已经绑定服务 mPendingInstalls.add(idx, params); if (idx == 0) { // 如果是第一个安装请求,则直接发送事件 MCS_BOUND 触发处理流程 mHandler.sendEmptyMessage(MCS_BOUND); } } break; } ... ... }
connectToService
假设我们是第一次走流程,还没有绑定服务,则会调用 connectToService() 方法,我们看下流程:
class PackageHandler extends Handler { 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); /** * bindServiceAsUser 方法会传入 mDefContainerConn, * bindServiceAsUser 方法的处理逻辑和我们调用 bindService 是类似的, * 服务建立连接后,会调用 onServiceConnected */ if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 如果绑定 DefaultContainerService 成功,mBound 会置为 ture mBound = true; return true; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false; }
这里可以看到 bind 到了一个 service ,这个 service 的 ComponentName 是 "DEFAULT_CONTAINER_COMPONENT" 这个常量,那我们就来看下这个 ComponentName。
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
所以我们知道 bind 的 service 是 DefaultContainerService 。绑定 DefaultContainerService 之后,设定进程的优先级为 THREAD_PRIORITY_DEFAULT。
然后等 bindServiceAsUser 这个方法执行完则又把线程的优先级设为 THREAD_PRIORITY_BACKGROUND。
我们这边要重点提到一个 mDefContainerConn 变量,研究一下:
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
这下我们知道了 mDefContainerConn 的类型是 DefaultContainerConnection ,那我们来看下 DefaultContainerConnection 这个类。
DefaultContainerConnection
// DefaultContainerConnection 实现了 ServiceConnection,所以在连接成功的时候会调用 onServiceConnected 方法 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)); // 发送了 MCS_BOUND 类型的消息 mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } public void onServiceDisconnected(ComponentName name) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); } }
上文我们提及到 mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM) 方法, 其实就是"绑定" DefaultContainerService。我们知道 bind 一个 Service ,其中负责通信的 ServiceConnection, 而本方法中负责通信的就是 mDefContainerConn。所以一旦绑定成功会执行 mDefContainerConn 的 onServiceConnected 方法。 而现实是当绑定成功后在 onServiceConnected 中将一个 IBinder 转换成了一个 IMediaContainerService。 这个就是 onServiceConnected 回调函数中根据参数传进来的 IMediaContainerService.Stub 的对象引用创建的一个远程代理对象, 后面 PacakgeManagerServic 通过该代理对象访问 DefaultContainerService 服务。
我们简单梳理一下以上代码所做的工作:
:sparkles: mBound 用于标识是否绑定了 DefaultContainerService,默认值为 false。
:sparkles: DefaultContainerService 是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此 DefaultContainerService 没有和 PMS 运行在同一进程中,它运行在 com.android.defcontainer 进程,通过 IMediaContainerService 和 PMS 进行 IPC 通信。
彼此之间的 IPC 通信如下图所示:
:sparkles: connectToService 方法用来绑定 DefaultContainerService。
:sparkles: mHandler.sendEmptyMessage(MCS_BOUND):发送 MCS_BOUND 类型的消息,触发处理第一个安装请求。
不知道你是否发现,有两个发送 MCS_BOUND 类型消息的方法:
// PackageHandler.doHandleMessage(已绑定服务) mHandler.sendEmptyMessage(MCS_BOUND); // 不带参数 // DefaultContainerConnection(未绑定服务 - 绑定服务) mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); // 带参数
MCS_BOUND 分析
不带参数
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) { // 服务没有绑定,则走这边,但是之前我们讲解过,发送 MCS_BOUND 时,已经绑定了服务,这显然是不正常的 if (!mBound) { // Something seriously wrong since we are not bound and we are not // waiting for connection. Bail out. 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 { // Should never happen ideally. Slog.w(TAG, "Empty queue"); } break; }
带参数
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) { ... ... // 走这边的逻辑,安装请求队列不为空 } else if (mPendingInstalls.size() > 0) { // 得到安装请求队列第一个请求 HandlerParams 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"); // 如果 HandlerParams 不为 null 就会调用 HandlerParams 的 startCopy 方法,用于开始复制 APK 的流程 if (params.startCopy()) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind..."); // 如果 APK 安装成功,删除本次安装请求 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"); // 如果还有其他的安装请求,接着发送 MCS_BOUND 消息继续处理剩余的安装请求 mHandler.sendEmptyMessage(MCS_BOUND); } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } // 如果安装请求数不大于 0 就会打印 “Empty queue” } else { // Should never happen ideally. Slog.w(TAG, "Empty queue"); } break; }
上面的流程其实很简单,我们根据是否传入了 ims 这个参数,走两条流程,核心的方法就是最终的 startCopy()。
复制 APK
上面我们提过,Copy APK 的操作是调用 HandlerParams 的 startCopy 方法。HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。
startCopy
private abstract class HandlerParams { private static final int MAX_RETRIES = 4; /** * Number of times startCopy() has been attempted and had a non-fatal * error. */ private int mRetries = 0; ... ... final boolean startCopy() { boolean res; try { if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); /** * mRetries 用于记录 startCopy 方法调用的次数,调用 startCopy 方法时会先自动加 1 * startCopy 方法尝试的次数超过了 4 次,就放弃这个安装请求 */ if (++mRetries > MAX_RETRIES) { Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); // 发送 MCS_GIVE_UP 类型消息,将第一个安装请求(本次安装请求)从安装请求队列 mPendingInstalls 中移除掉 mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false; } else { handleStartCopy(); // :boom: :boom: :boom: 重点方法 :boom: :boom: :boom: res = true; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } // 调用 handleReturnCode 抽象方法,这个方法会在 handleStartCopy 执行完拷贝相关行为之后,根据 handleStartCopy 做进一步的处理,主要返回状态码 handleReturnCode(); return res; } ... ... abstract void handleStartCopy() throws RemoteException; abstract void handleServiceError(); abstract void handleReturnCode(); }
这边我们还是简单的看一下 MCS_GIVE_UP 和 MCS_RECONNECT 两种 message 的处理流程,逻辑相当简单:
MCS_RECONNECT
case MCS_RECONNECT: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect"); if (mPendingInstalls.size() > 0) { if (mBound) { disconnectService(); } if (!connectToService()) { Slog.e(TAG, "Failed to bind to media container service"); for (HandlerParams params : mPendingInstalls) { // Indicate service bind error params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); } mPendingInstalls.clear(); } } break; }
判断安装请求队列 mPendingInstalls 是否还有元素,如果有元素先断开绑定,则再次重新调用 connectToService 方法,我们知道 connectToService() 内部会再次执行绑定 DefaultContainerService,而在绑定成功后会再次发送一个 what 值为 MCS_BOUND 的 Message,从而又回到了 startCopy 里面。
MCS_GIVE_UP
case MCS_GIVE_UP: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries"); HandlerParams params = mPendingInstalls.remove(0); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); break; }
直接删除了安装请求队列 mPendingInstalls 里面下标为 0 的元素,即取消本次安装请求。
handleStartCopy
我们发现 handleStartCopy 也是一个抽象的方法,那么它在哪实现?前面我们说过:HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。
class InstallParams extends HandlerParams { /* * Invoke remote method to get package information and install * location values. Override install location based on default * policy if needed and then create install arguments based * on the install location. */ public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; ... ... /** * 确定 APK 的安装位置 * onSd: 安装到 SD 卡 * onInt: 内部存储即 Data 分区 * ephemeral:安装到临时存储 */ 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; // APK 不能同时安装在 SD 卡和 Data 分区 if (onInt && onSd) { // Check if both bits are set. Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; // 安装标志冲突,Instant Apps 不能安装到 SD 卡中 } else if (onSd && ephemeral) { Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { /** * 获取 APK 的少量的信息 * 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 getMinimalPackageInfo 方法, * 该方法轻量解析 APK 并得到 APK 的少量信息, * 轻量解析的原因是这里不需要得到 APK 的全部信息,APK 的少量信息会封装到 PackageInfoLite 中。 */ 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) { ... ... } else { installFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite, mSettings.mPackages, getUser()); loc = installLocationPolicy(pkgLite); ... ... } } /** * 根据 InstallParams 创建 InstallArgs 对象 * InstallArgs 是一个抽象类,定义了 APK 的安装逻辑,比如"复制"和"重命名" APK 等 * * abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException; * * InstallArgs 有 3 个子类,都被定义在 PMS 中: * FileInstallArgs:用于处理安装到非 ASEC 的存储空间的 APK ,也就是内部存储空间(Data分区) * AsecInstallArgs:用于处理安装到 ASEC 中(mnt/asec)即 SD 卡中的 APK * MoveInstallArgs:用于处理已安装 APK 的移动的逻辑 */ final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { ... ... if (!origin.existing && requiredUid != -1 && isVerificationEnabled( verifierUser.getIdentifier(), installFlags, installerUid)) { ... ... } else { // 对 APK 进行检查后就会调用 InstallArgs 的 copyApk 方法进行安装 ret = args.copyApk(mContainerService, true); } } mRet = ret; } ... ... }
FileInstallArgs
OK,我们知道 InstallParams 有三个子类,不同的 InstallArgs 子类会有着不同的处理,那我们现在以 FileInstallArgs 为例跟踪学习一下具体的流程:
copyApk
/** * Logic to handle installation of non-ASEC applications, including copying * and renaming logic. */ class FileInstallArgs extends InstallArgs { private File codeFile; ... ... int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk"); try { return doCopyApk(imcs, temp); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
doCopyApk
调用了 doCopyApk 方法:
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { ... ... try { final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0; // 用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp ,其中 18300388 是安装的 sessionId 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; /** * 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 copyPackage 方法, * 这个方法会在 DefaultContainerService 所在的进程中将 APK 复制到临时存储目录, * 比如 /data/app/vmdl18300388.tmp/base.apk 。 */ ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 真正的文件拷贝 ... ... return ret; }
安装 APK
handleReturnCode
我们回到 APK 的复制调用链的头部方法:HandlerParams 的 startCopy 方法,在最后 调用了 handleReturnCode 方法,进行 APK 的安装。
private abstract class HandlerParams { private static final int MAX_RETRIES = 4; private int mRetries = 0; ... ... final boolean startCopy() { boolean res; try { if (++mRetries > MAX_RETRIES) { ... ... } else { handleStartCopy(); res = true; } } catch (RemoteException e) { ... ... } // 处理复制 APK 后的安装 APK 逻辑 handleReturnCode(); // :boom: :boom: :boom: :boom: :boom: :boom: return res; } ... ... abstract void handleReturnCode(); }
handleReturnCode 也是一个抽象方法,那么在哪里实现?同样,它的实现在 InstallParams 中。
@Override void handleReturnCode() { // If mArgs is null, then MCS couldn't be reached. When it // reconnects, it will try again to install. At that point, this // will succeed. if (mArgs != null) { // "装载代码"的入口是 processPendingInstall(InstallArgs,int) 方法 processPendingInstall(mArgs, mRet); } }
我们发现调用了 processPendingInstall 方法,继续跟!
processPendingInstall
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) { /** * 安装前处理 * 用于检查 APK 的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的 APK 文件 args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageTracedLI(args, res); // :boom: :boom: :boom: :boom: :boom: :boom: } /** * 安装后收尾 * 用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件 args.doPostInstall(res.returnCode, res.uid); } ... ... } }); }
installPackageTracedLI
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage"); installPackageLI(args, res); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
installPackageLI
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 { // 解析 APK pkg = pp.parsePackage(tmpPackageFile, parseFlags); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } ... ... // Get rid of all references to package scan path via parser. pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // 检查 APK 是否存在 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); // 查看 Settings 中是否存有要安装的 APK 的信息,如果有就获取签名信息 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 || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) { if (onExternal) { // 系统APP不能在SD卡上替换安装 res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard"); return; } else if (instantApp) { // 系统 APP 不能被 Instant App 替换 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; } if (!instantApp) { startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); } else { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName); } } try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI")) { if (replace) { // 替换安装 if (pkg.applicationInfo.isStaticSharedLibrary()) { 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 { // 安装新的 APK 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 /*updateAvailable*/); } ... ... } }
installPackageLI 方法的代码很长,这里截取主要的部分,主要做了几件事:
:sparkles: 1、创建 PackageParser 解析 APK 。
:sparkles: 2、检查 APK 是否存在,如果存在就获取此前没被改名前的包名,赋值给 PackageParser.Package 类型的 pkg ,将标志位 replace 置为 true 表示是替换安装。
:sparkles: 3、如果 Settings 中保存有要安装的 APK 的信息,说明此前安装过该 APK ,则需要校验 APK 的签名信息,确保安全的进行替换。
:sparkles: 4、将临时文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名为 /data/app/包名-1/base.apk 。这个新命名的包名会带上一个数字后缀 1,每次升级一个已有的 App ,这个数字会不断的累加。
:sparkles: 5、系统 APP 的更新安装会有两个限制,一个是系统 APP 不能在 SD 卡上替换安装,另一个是系统 APP 不能被 Instant App 替换。
:sparkles: 6、根据 replace 来做区分,如果是替换安装就会调用 replacePackageLIF 方法,其方法内部还会对系统 APP 和非系统 APP 进行区分处理,如果是新安装 APK 会调用 installNewPackageLIF 方法。
installNewPackageLIF
我们以安装新 APK 为例,查看 installNewPackageLIF 的源码:
/* * Install a non-existing package. */ 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"); ... ... try { // 扫描 APK PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user); // 更新 Settings 信息 updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // 安装成功后,为新安装的应用程序准备数据 prepareAppDataAfterInstallLIF(newPackage); } else { // 安装失败则删除 APK 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 件事:
:sparkles: 1、扫描 APK,将 APK 的信息存储在 PackageParser.Package 类型的 newPackage 中,一个 Package 的信息包含了 1 个 base APK 以及 0 个或者多个 split APK 。
:sparkles: 2、更新该 APK 对应的 Settings 信息,Settings 用于保存所有包的动态设置。
:sparkles: 3、如果安装成功就为新安装的应用程序准备数据,安装失败就删除APK。
scanPackageTracedLI
调用 scanPackageTracedLI() 进行安装 :
public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); try { return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
scanPackageLI - 01
scanPackageTracedLI() 调用了 scanPackageLI() 方法:
/** * Scans a package and returns the newly parsed package. * Returns {@code null} in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); pp.setCallback(mPackageParserCallback); ... ... return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user); }
scanPackageLI - 02
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { boolean success = false; try { // scanPackageDirtyLI 实际安装 package 的方法 final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags, currentTime, user); success = true; return res; } finally { if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) { // DELETE_DATA_ON_FAILURES is only used by frozen paths destroyAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); destroyAppProfilesLIF(pkg, UserHandle.USER_ALL); } } }
总结
本文主要讲解了 PMS 是如何处理 APK 安装的流程,主要有几个步骤:
:sparkles: PackageInstaller 安装 APK 时会将 APK 的信息交由 PMS 处理,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作。
:sparkles: PMS 发送 INIT_COPY 和 MCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService ,完成复制 APK 等工作。
:sparkles: 复制 APK 完成后,会开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作。
参考
以上所述就是小编给大家介绍的《Android 8.1 源码_核心篇 -- 深入研究 PMS 系列(6)之 APK 安装流程(PMS)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 深入理解 FilterChainProxy【源码篇】
- 深入理解 WebSecurityConfigurerAdapter【源码篇】
- 深入koa2源码
- 深入理解channel:设计+源码
- 深入浅出Semaphore源码解析
- 深入剖析Vue源码 - 组件基础
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
运营有道:重新定义互联网运营
李明轩 / 机械工业出版社 / 2017-7-31 / 69.00元
本书是前百度资深运营专家多年运营经验的总结,是作者运营千万级用户规模的大型互联网产品的实操经验复盘,是作者在“在行”上为近百位CEO和高管提供互联网运营咨询服务后对互联网运营需求的深入洞见。 本书的思想基础是“运营必须以用户为中心”,从产品、用户、市场3个维度对互联网运营重新进行了系统性的梳理:从道的层面解读并重新定义运营方法论,从术的层面围绕方法论提出行之有效的解决方法和实际案例。重点不在......一起来看看 《运营有道:重新定义互联网运营》 这本书的介绍吧!