Android M PackageManager对于应用程序apk的安装流程分析
上篇文章我们分析了android M PackageManagerService 启动过程,今天我们继续深入了解下对于apk的安装,PackageManager又是怎样做的呢?
应用程序安装有很多种方法,开发者最常见的就是使用adb install命令或者pm install脚本命令。
很多的应用市场也都有自己的封装,android原生的安装应用为PackageInsteller。
本篇涉及源码路径:
system/core/adb/ commandline.cpp
frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
frameworks/base/packages/DefaultContainerService/
frameworks/base/core/java/android/content/pm/PackageParser.java
adb install原理
我们先看adb install原理
adb install命令来源于/android/system/core/adb工具,从上面的代码看到,adb install实际执行的是install_app()。
Install_app会根据存储路径将apk文件copy到/data/local/temp或者sd卡中的路径,后面进行apk复制时会从这个路径里面取。
这里我们可以看到adb命令最终调用的是pm脚本来进行apk的安装。
PM脚本
pm是android中的一个脚本,由pm.java编译生成,最终被放到system/bin下的,其代码路径为:
frameworks/base/cmds/pm
pm脚本的main方法中创建一个Pm对象,然后调用其run方法跑起来。
run方法中获取到package包管理器的binder客户端本地代理Proxy,就可以和IPackageManager服务端通信了。
这里还通过binder通信获取到之前在PKMS构造函数最后完成时创建的PackageInstallerService服务,后面安装时会用到它。
接着,解析arg参数,匹配到install时,会执行runInstall。
这里先创建了一个LocalPackageInstallObserver,这个obs将用于接收PKMS返回的安装消息。
最终,我们通过installPackageAsUser调用到了PKMS中。
怎么过去的?
this指的是mRemote, mRemote即是package的服务端PKMS。
installPackageAsUser方法
前面讲到PM脚本通过binder将安装命令发送到了PKMS中,接下来看PKMS是怎样进行apk安装的。
说明:
- 检查是否具有安装权限,或者是否用户禁止安装等;
- 封装一个INIT_COPY的消息,并且创建InstallParams对象,然后把消息通过handler发送出去;
根据PKMS启动过程,我们知道,之前创建了PackageHandler,且其消息处理线程已经运行起来,很确定,INIT_COPY就是在PackageHandler的handleMessage中处理的。
PackageHandler的handleMessage方法直接调用的是doHandleMessage,根据msg类型匹配到INIT_COPY分支。
Apk的安装需要依赖一个apk提供的服务来支持,这个服务就是DefaultContainerService,即源码中的DefaultCotainerService.apk。
第一次安装的时候,由于服务还未启动,需要bindService来启动该服务,如果已经启动,则将安装信息通过MCS_BOUND触发。
关于DefaultContainerService怎么启动就不说了哈,我们直接看对MCS_BOUND消息的处理。
在MCS_BOUND中,调用了startCopy方法。
startCopy方法中会紧接着调用handleStartCopy,这个方法是个抽象方法,具体的实现由子类完成,之前我们创建的handleParams为InstallParams,所以这里会调用到InstallParams的handleStartCopy方法中,如果安装完成,则调用handleReturnCode,返回处理结果。
handleStartCopy
说明:
- DCS的getMinimalPackageInfo方法会去解析apk文件,得到一个PackageLite对象,该对象是一个轻量级(相对于pkg)的用于描述APK的结构,这里面会取到recommendedInstallLocation,表示apk推荐的安装路径。
- 计算apk的大小,检查存储空间是否足够;
- installLocationPolicy检查检查推荐的安装路径,例如系统apk不允许安装在SD卡上;
- createInstallArgs将根据不同的安装位置创建不同的InstallArgs,如果是内部存储则返回FileInstallArgs,外部则是AsecInstallArgs(SD卡),其中还有负责移动的MoveInstallArgs;
- 最后调用args的copyApk开始进行apk的拷贝;
copyApk
通过前面的分析,我们已经得到apk适合的安装位置,接下来就由copyApk来完成apk的复制工作。
说明:
- 首先会通过installService的方法allocateStageDirLegacy在/data/app下创建临时文件,临时文件名为 临时文件名为vmdl-随机数.tmp;
- 调用DCS的copyFile执行复制操作,最终将/data/local/temp下的apk文件复制到/data/app/下,文件名为base.apk, 然后修改相关文件的读写权限等。
- 调用copyNativeBinariesWithOverride对lib库进行复制;
DefContainer:Copying /data/app/com.iflytek.inputmethod.gionee-1/base.apk to base.apk
至此,handleStartCopy就完成了,apk拷贝完了,其实apk并没有完全解析并且归档到PKMS的数据结构中,因此,接下来我们还要对apk文件解析归档。
handleReturnCode
由于最初创建的handleParams为InstallParams,所以handleReturnCode也是installParams的子类方法。
该方法中mArgs为FileInstallArgs,所以会直接调用processPendingInstall进行接下来的处理。
说明:
- 在mHandler中抛一个Runnable对象,如果之前handleStartCopy中没有出现问题,则返回码应该是INSTALL_SUCCEEDED,就会调用doPreInstall对安装路径进行清理,如果是success,则什么都不做;
- 调用installPackageLI进行扫描安装,即我们之前的scanPackageLI ==> parsePackage==> parseBaseApk ==> parseBaseApplication…
- 最后会调用doRename对文件进行命名;
- 调用doPostInstall进行安装文件的处理。
- 如果允许backup则通过BackupManager对其进行备份处理;
- 向handr抛一个POST_INSTALL消息;
进入POST_INSTALL消息处理环节:
说明:
- 依次发送PACKAGE_ADDED,ACTION_PACKAGE_REPLACED广播;
- 强制执行依次GC进行资源回收;
- 调用FileInstallArgs的doPostDeleteLI进行资源清理;
- 调用onPackageInstalled向PM通知安装的结果(obs);
之前的obs在调用完install请求后,进入wait状态等待PKMS的返回,在onPackageInstalled方法中notifyAll将其唤醒输出安装结果。
安装返回值结果
常见的APK安装结果(返回值)通常有以下几种。
返回值 | 说明 | 解决方式 |
INSTALL_SUCCEEDED | 安装成功 | - |
INSTALL_FAILED_ALREADY_EXISTS | apk已经安装过 | 加-r参数 |
INSTALL_FAILED_INVALID_APK | apk安装包无效 | 确认apk文件是否有效 |
INSTALL_FAILED_INVALID_URI | uri无效,路径不对.. | 确认apk的绝对路径 |
INSTALL_FAILED_INSUFFICIENT_STORAGE | 空间不足,中间文件vmdl无法创建 | 清除空间 |
INSTALL_FAILED_DUPLICATE_PACKAGE | 主要检查版本号 |
|
INSTALL_FAILED_NO_SHARED_USER | 请求的SharedUserId不存在 |
|
INSTALL_FAILED_UPDATE_INCOMPATIBLE | 与之前安装的签名不对 | 将之前的app删除后再安装 |
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE | 与sharedUserId的签名不匹配 |
|
INSTALL_FAILED_MISSING_SHARED_LIBRARY | 使用的共享库不存在 | 确认所使用的共享库系统是否已支持。 |
INSTALL_FAILED_OLDER_SDK INSTALL_FAILED_NEWER_SDK | Sdk版本不匹配<uses-sdk> | 请使用正确的targetsdk |
INSTALL_FAILED_CONFLICTING_PROVIDER | provider冲突 | 确认manifest中使用的provider |
INSTALL_FAILED_TEST_ONLY | android:testOnly="true" | Android studio中请使用build生成apk安装,或者adb install –t参数 |
INSTALL_FAILED_VERSION_DOWNGRADE | VersionCode低了 |
|
……………… |
|
|
安装流程小结
至此,apk的安装流程就基本上完成了,下面的流程图可以完整展现该过程。
后续将会继续分析权限管理部分,欢迎关注。