本文首发于微信公众号「刘望舒」
关联系列
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);//1
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);//2
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
...
mHandler.sendMessage(msg);//3
}
注释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);
//mBound用于标识是否绑定了服务,默认值为false
if (!mBound) {
//1
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
//如果没有绑定服务,重新绑定,connectToService方法内部如果绑定成功会将mBound置为true
if (!connectToService()) {
//2
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) {
mHandler.sendEmptyMessage(MCS_BOUND);//3
}
}
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)) {
//1
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;//2
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