启动安装
在app代码中,如果想安装另一个应用,一般通过下列代码实现,调用后会显示系统安装界面
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType("the apkPath of app...",
"application/vnd.android.package-archive");
startActivity(intent);
系统安装界面是com.android.packageinstaller.PackageInstallerActivity,它在manifest里的声明是
<activity android:name=".PackageInstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="package" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
可以看到这个activity支持三种action,VIEW、INSTALL_PACKAGE、CONFIRM_PERMISSIONS,其中VIEW和INSTALL_PACKAGE都可以启动应用的安装。
PackageInstallerActivity根据用户设置(比如是否允许安装未知来源的应用等)进行一些检查,同时查询应用需要的权限进行显示,在用户点击“安装”以后,启动界面InstallAppProgress进行安装。这里整个流程比较简单,不再贴代码进行分析了,不过其中获取应用信息、获取权限信息的调用可以学习一下。
在流程的最后,调用了PMS.installPackageWithVerificationAndEncryption()正式进入安装流程
PMS安装流程
通过context.getPackageManager得到的PackageManager实际上是ApplicationPackageManager的实例,所以调用到的就是
/**
* packageURI -- 要安装的apk的uri
* obesrver -- 安装过程的观察者
* flags -- 这个流程里,表示是否覆盖安装,当然其他流程中会有其他值
* installerPackageName -- 安装者的包名
* verificationParams -- 一些校验参数,后续流程中看它的具体使用
* encryptionParams -- 一些加密参数,这个流程里是null
**/
@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
IPackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags,
installerPackageName, verificationParams, encryptionParams);
}
installCommon在校验参数之后调用了PMS.installPackage,然后调用到PMS.installPackageAsUser
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
// 主要做几个事情,1、检查安装者权限;2、更新flag;3、创建UserHandle实例;4、创建OriginInfo
// ...
// 这里的Handler是内部类PackageHandler
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
mHandler.sendMessage(msg);
}
PackageHandler的message处理方法如下,这里先仅列出两个事件的处理
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (!mBound) {
if (!connectToService()) {
params.serviceError();
return;
} else {
mPendingInstalls.add(idx, params);
}
} else {
mPendingInstalls.add(idx, params);
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
case MCS_BOUND: {
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
if (!mBound) {
// 异常情况处理
}
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) {
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
// 异常情况,打印log
}
break;
}
}
}
仔细看一下,安装过程是需要调用IMediaContainerService mContainerService的,在第一次调用到INIT_COPY的时候还没有bindService,所以mBound是false,mContainerService的是空,所以要先调用connectToService,然后把安装参数add到pending队列,在bindService成功后,会发送MCS_BOUND事件,mContainerService会被赋值为DefaultContainerService.IMediaContainerService.Stub实例,然后开始处理pending队列。
真正的处理从InstallParams.startCopy()开始,依次调用handleStartCopy()和handleReturnCode()
final boolean startCopy() {
boolean res;
try {
if (++mRetries > MAX_RETRIES) {
// 超过重试次数
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
// ...
}
handleReturnCode();
return res;
}
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
i