目录见上↑↑↑,整个安装过程可分为三步:
1.权限检查2.复制文件
3.装载应用
1.权限检查
调用installPackageAsUser函数
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int callingUid = Binder.getCallingUid();// 利用binder机制,获取安装发起进程的uid
//检查权限 该函数主要是检查进程是否有权限安装 展开见1.1
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
//检查当前用户是否具备安装app的权限
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
//如果是发起端进程是shell或者root,那么添加flags:PackageManager.INSTALL_FROM_ADB
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// 从flags中去掉INSTALL_FROM_ADB和INSTALL_ALL_USERS
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;//创建一个当前用户的handle
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
// Only system components can circumvent runtime permissions when installing.
// Android 6.0 当权限属于运行时权限时,需要弹出框,让用户授权,对于system app,应该取消运行时权限弹框授权,而是直接授权。
// 那么就要在system app中加入INSTALL_GRANT_RUNTIME_PERMISSIONS;安装第三方app,当然没有INSTALL_GRANT_RUNTIME_PERMISSIONS了
if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);//后续判断APK安装到哪里时,会用到
//构造InstallParams,注意packageAbiOverride为null,然后利用Android中的Handler机制,发送给相关的线程进行安装。
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
mHandler.sendMessage(msg);
}
其主要流程如图所示:
1.1 enforceCrossUserPermission
installPackageAsUser方法的前部调用了enforceCrossUserPermission,代码如下:
void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission,
boolean checkShell, String message) {
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
}
//当前userid和发起者进程所属的userid一致,那么OK,直接返回
// 我们现在就属于这种情况
if (userId == UserHandle.getUserId(callingUid)) return;
//不一致,那就要看是不是SYSTEM进程了,依旧不是,那么执行下逻辑,抛异常
if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
if (requireFullPermission) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
} else {
try {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
} catch (SecurityException se) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS, message);
}
}
}
}
2.复制文件
权限检查中,installPackageAsUser方法尾部会发送一个INIT_COPY消息,接着会使用doHandleMessage方法处理收到的消息:
2.1 INIT_COPY
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;//取出InstallParams
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// If a bind was already initiated we dont really need to do anything.
// The pending install will be processed later on.
if (!mBound) {
// If this is the only one pending we might have to bind to the service again.
// 将绑定DefaultContainerService服务
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();//连接服务失败
return;
} else {
// Once we bind to the service, the first pending request will be processed.
// 连接成功,把安装信息保存到mPendingInstalls中,等待收到连接的返回消息后,再继续安装
mPendingInstalls.add(idx, params);
}
} else {
// 加入安装信息
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
//如果mPendingInstalls中只有一项,那么立即发送MCS_BOUND消息
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
...
case 巴拉巴拉
}
}
INIT_COPY消息的处理中将绑定DefaultContainerService ,因为这是一个异步的过程,要等待的绑定的结果通过onServiceConnected()返回,所以这里就将安装的参数信息放到了mPendingInstalls列表中。
如果这个Service之前就绑定好了,现在就不要再次绑定了,安装信息同样要放到mPendingInstalls中。
如果有多个安装请求同时到达,就可以通过mPendingInstalls列表对它们进行排队。
如果列表中只有一项,说明没有更多的安装请求,因此这种情况下,需要立即发出MCS_BOUND消息,进入下一步的处理。(应该有个INIT_COPY的流程图 简书那个)
2.1.1 connectToService
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.OWNER)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
绑定成功后在onServiceConnected中将一个IBinder转换成了一个IMediaContainerService。这个就是在onServiceConnected回调函数中根据参数传进来的IMediaContainerService.Stub的对象引用创建的一个远程代理对象。以后PMS务通过该代理对象访问DefaultContainerService服务。
2.2 MCS_BOUND
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mCon