第三章 Android PackageManager

2.3. PackageInstaller

PackageInstaller是Android提供的默认应用程序安装与卸载管理应用程序。应用包中有两个Activity:PackageInstallerActivity和UninstallerActivity分别用来管理应用程序的安装和卸载。这两个Activity可以通过发送Intent的方式启动。下面的代码片段指出了两个Activity能处理的Intent:

android:configChanges="orientation|keyboardHidden|screenSize"

android:excludeFromRecents="true">

android:configChanges="orientation|keyboardHidden|screenSize"

android:excludeFromRecents="true"

android:theme="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">

通过给PackageInstallerActivity发送"android.intent.action.VIEW"或者是"android.intent.action.INSTALL_PACKAGE" Intent就能启动对一个APK文件的安装过程。例如,我们假设有一个应用程序位于/mnt/sdcard/Test.apk,为了启动对这个应用程序的安装,可以通过文件浏览器点击这个应用包文件,同时也可以通过adb shell登陆机器,然后输入以下命令:

am start -a android.intent.action.VIEW -t application/vnd.android.package-archive -c android.intent.category.DEFAULT -d file:///mnt/sdcard/Test.apk

当输入上述命令后,首先是以Action等参数构造一个Intent,然后am调用ActivityManagerService的startActivity方法。最后的结果是启动PackageInstallerActivity,并将构造的Intent传递给启动的Activity。下面简单的描述下,PackageInstallerActivity安装应用的过程:

首先PackageInstaller会检查应用程序的完成性,尝试着从应用包中提解析AndroidManifest.xml文件,构造ParckageParser.Package对象。如果成功,则继续安装;否则安装退出

然后对发起安装请求的应用属性进行判断:如果是system应用,那么就进入初始化安装步骤;如果是非system应用,那么存在一个验证是否允许安装“unknown source”的步骤。

如果上面的步骤通过,就进入了初始化安装过程。初始化过程主要是检测应用是否安装,以及提取相关的权限信息,用于界面显示。这一过程主要是UI界面相关,略过不表

当用户在安装界面上点击“确定”按钮,PackageInstallerActivity就会启动InstallAppProgress,在此Activity会调用PackageManager的方法public abstract void installPackageWithVerificationAndEncryption(Uri packageURI,

IPackageInstallObserver observer, int flags, String installerPackageName,

VerificationParams verificationParams,

ContainerEncryptionParams encryptionParams)真正进行安装,并显示安装进度。

2.3.1. installPackageWithVerificationAndEncryption

PackageManager的installPackageWithVerificationAndEncryption最终是一个binder调用,是在PackageManagerService中负责实现。在此方法中,获取一个Message对象,其id为INIT_COPY,其obj为InstallParams。其中InstallParams对象根据installPackageWithVerificationAndEncryption的方法参数构造。INIT_COPY消息是在PackageHandler中进行处理。对于此消息的处理流程为:

首先通过connectToService方法绑定IMediaContainerService服务;在绑定成功之后,发送MCS_BOUND消息;IMeidaContainerService的实现是在frameworks/base/packages/DefaultContainerService/src/com/android/def/DefaultContainerService.java中。它负责原始apk文件的解密工作。

在PackageHandler消息中再次对MCS_BOUND消息进行处理。这次就会调用InstallParams的startCopy方法,开启复制过程。startcopy方法又会调用handleStartCopy进行复制工作。主要做了如下工作:

确定APK的安装位置

检查磁盘空间是否足够

以InstallParams构造一份InstallArgs对象。InstallArgs有两个子类:AsecInstallArgs和FileInstallArgs,对于安装于外部sd卡或者是有Forward Lock属性的应用,将会构造AsecInstallArgs,否则构造的是FileInstallArgs对象。

检查APK是否需要验证(一般是不需要)

调用InstallArgs对象的copyApk方法。

所以,handleStartCopy最重要的工作就是由InstallParams的copyApk来完成。假设在这个InstallParams引用是一个FileInstallArgs对象,那么其copy流程如下:

创建临时文件。对于最终安装在/data/app目录下面的应用,其临时文件的共享名为:/data/app/vmdl-XXX.tmp。这个XXX是一个随机数

调用IMediaContainerService的服务接口,从原始APK文件中提取出解密过的APK数据,写入到临时文件中

拷贝jni动态库到相应的目录

在InstallParams的startCopy处理完handleStartCopy之后,会调用handleRetureCode方法,此方法中将会进行触发真正的安装流程。

void handleReturnCode() {

if (mArgs != null) {

processPendingInstall(mArgs, mRet);

if (mTempPackage != null) {

if (!mTempPackage.delete()) {

Slog.w(TAG, "Couldn't delete temporary file: " +

mTempPackage.getAbsolutePath());

}

}

}

}

在handleReturnCode中又调用了processPendingInstall方法,这个方法主要做了以下几件事情:

调用FileInstallArgs对象的doPreInstall方法:如果之前的流程有错误,此方法将会执行清理动作

调用PackageManagerService的installPackageLI方法,开始安装过程

调用FileInstallArgs对象的doPostInstall方法:如果之前的流程有错误,此方法将会执行清理动作

installPackgeLI执行步骤如下:

从临时文件中提取出Package对象:final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,

null, mMetrics, parseFlags)

提取证书信息,并验证

将临时文件更名为正式apk文件,修改动态库目录名

调用installNewPackageLI安装正式apk文件

scanPackageLI方法负责安装apk

updateSettingsLI负责更新“记账簿”

Top