Android6.0 PackageManagerService(PMS)-安装

本文详细介绍了Android 6.0中PackageManagerService(PMS)的安装过程,主要分为三个步骤:1)权限检查,调用enforceCrossUserPermission;2)复制文件,包括INIT_COPY、MCS_BOUND等阶段;3)装载应用,涉及handleReturnCode、installPackageLI等关键函数。整个安装过程中,文件复制和权限管理是核心环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

    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
<think>嗯,用户想在Android系统中实现静默安装,也就是安装应用时不弹出提示框。首先,我需要回忆一下Android的权限机制。根据引用[2],从Android Q开始,应用无法查看权限在UI中的分组情况,这可能涉及到权限管理的严格化。静默安装通常需要系统级别的权限,比如系统应用或设备管理员权限。 然后,用户提到引用[3]中关于安装前权限检查和PMS(Package Manager Service)的部分,说明安装流程在检查之后才会进行实际安装。静默安装可能需要绕过这些检查,或者有更高的权限直接调用PMS的接口。 另外,引用[1]提到使用InstallShield Silent执行静默安装,但这是在Windows环境下的方法,可能不适用于AndroidAndroid系统需要不同的实现方式。 接下来,我需要考虑可能的实现方法。首先,系统应用可能需要使用`INSTALL_PACKAGES`权限,但普通应用无法声明该权限。只有系统应用或使用平台签名才能获得。另外,设备管理员或使用adb命令也可能是一种方法,但需要用户授权。 还有一种可能是使用隐藏的API,但这种方法在Android高版本中可能被限制,且兼容性差。另外,root设备后使用pm命令安装,但需要root权限,普通用户可能无法接受。 还要注意,从Android 10开始,后台安装应用的限制更加严格,可能需要前台服务或者用户交互。所以,可能需要在系统应用或定制ROM中实现静默安装。 总结可能的步骤:1. 成为系统应用;2. 使用DeviceOwner权限;3. 使用adb命令;4. root设备。同时,需要考虑Android版本的限制,尤其是Android Q之后的变更。 最后,需要提醒用户遵循Google的政策,避免滥用权限,确保合法合规。可能还需要引用相关的文档或开发者资源,比如Android源码中的PackageManagerService部分。</think>在Android系统中实现静默安装(不弹出用户确认对话框)需要特定权限和系统级支持,以下是关键实现路径及注意事项: ### 一、静默安装的核心条件 1. **系统应用权限** 需声明`android.permission.INSTALL_PACKAGES`权限,但此权限仅对以下情况开放: - 使用平台证书签名的应用(如系统预装应用) - 设备拥有者(Device Owner)应用 - 通过adb命令安装时使用`-g`参数授予所有权限[^3] 2. **绕过PMS检查** 安装流程需直接调用`PackageManagerService`的`installPackage`方法,跳过`InstallStart`阶段的用户交互验证 ### 二、具体实现方法 #### 方法1:系统级应用实现 ```java // 需要系统签名或设备管理员权限 public boolean silentInstall(File apkFile) { try { PackageManager pm = context.getPackageManager(); Method installMethod = pm.getClass().getMethod("installPackage", Uri.class, IPackageInstallObserver.class, int.class, String.class); installMethod.invoke(pm, Uri.fromFile(apkFile), null, PackageManager.INSTALL_REPLACE_EXISTING, null); return true; } catch (Exception e) { Log.e("SilentInstall", "Error: " + e.getMessage()); return false; } } ``` #### 方法2:ADB命令实现(需root权限) ```bash # 通过shell命令安装 adb shell pm install -r /sdcard/app.apk # 卸载同理 adb shell pm uninstall com.example.package ``` ### 三、版本限制与兼容性 | Android版本 | 限制条件 | |------------|----------| | < 5.0 | 可通过反射调用隐藏API | | ≥ 6.0 | 需要动态授予`WRITE_EXTERNAL_STORAGE`权限 | | ≥ 10 (Q) | 禁止后台安装,需前台服务或用户交互[^2] | | ≥ 11 (R) | 强化静默安装限制,需`REQUEST_INSTALL_PACKAGES`权限 | ### 四、风险提示 1. 非系统应用尝试静默安装可能触发`SecurityException` 2. Google Play政策禁止非授权安装行为 3. Android 10+限制后台安装的特性需要特殊适配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值