Android6.0 PackageManagerService(PMS)-安装

目录见上↑↑↑整个安装过程可分为三步:

    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
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值