pms包管理服务分析-外部安装流程(adb/packageInstaller)

系统开机过程中扫描并安装apk的过程是针对已有的apk文件。针对外部apk的安装,pms提供了另外的接口。我们一般也就通过两种方式去安装外部apk,一种是通过adbinstall命令安装外部应用。另外一种是通过系统应用PackageInstaller,通过界面引导的方式安装外部应用。下面分别来分析这两种安装方式的过程和相同的地方。

通过adb install命令安装外部应用

adb install命令实际上调用的是/system/bin目录下的pm可执行程序来实现安装应用的功能。

下面来分析下pm的实现源码:

[/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java]

public static void main(String[] args) {
	   int exitCode = 1;
	   try {
		exitCode = new Pm().run(args);
	   } catch (Exception e) {		
	   }
	   System.exit(exitCode);
}     	

Pm程序的入口main函数中,创建一个Pm的实例并执行run方法,传入args参数(含apk路径等信息)。继续分析run方法:

    public int run(String[] args) throws IOException, RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }
        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
		...
        String op = args[0];
        if ("install".equals(op)) {
            return runInstall();
        }
		...
	}

run方法中首先读取第一个参数args[0],第一个参数是op(operation),表示命令的操作是什么,这里我们来看op=”install”的情况,直接调用runInstall方法,下面来看看runInstall方法的实现:

private int runInstall() {
        int installFlags = 0;
        int userId = UserHandle.USER_ALL;
        String installerPackageName = null;
		...
        try {
            VerificationParams verificationParams = new VerificationParams(verificationURI,
                    originatingURI, referrerURI, VerificationParams.NO_UID, null);
            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                    installerPackageName, verificationParams, abi, userId);
			...
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }

可以看到runInstall方法最终通过binderpms通信,执行pmsinstallPackageAsUser方法,分别传入apk的文件路径,abi信息等。pmsinstallPackageAsUser方法在接下来会分析。

通过PackageInstaller应用安装外部应用

查看PackageInstaller安装界面代码:

[/package/app/PackageInstaller/src/com/android/packageinstaller /InstallAppProgress.java]

    public void initView() {
        setContentView(R.layout.op_progress);
        int installFlags = 0;
        PackageManager pm = getPackageManager();
        ...
        pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
        ...
    }

PackageInstaller安装界面初始化的时候就直接调用PackageManager提供的installPackageWithVerificationAndEncryption方法执行安装操作。下面来分析下installPackageWithVerificationAndEncryption的具体实现:

[/frameworks/base/core/java/android/app/ApplicationPackageManager.java]

    public void installPackageWithVerificationAndEncryption(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
     installCommon(packageURI, observer, flags, installerPackageName, verificationParams,encryptionParams);
    }

继续分析installCommon方法:

    private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }
        final String originPath = packageURI.getPath();
        try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }
    }

installCommon方法首先判断传入的Uri是否属于文件类型,如果不是,直接异常返回。如果是,则通过Uri获得apk文件路径,最后调用pmsinstallPackage方法(实际上同样是调用installPackageAsUser)执行具体的安装操作。


installPackageAsUser方法分析

上面两种主要的外部安装路径最后都指向了pmsinstallPakcageAsUser方法。这个方法提供外部安装功能,我们从应用安装的第一个阶段(复制文件)开始

[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]

    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);

第一步先检查调用端是否具有INSTALL_PACKAGES权限,如果没有该权限则抛出权限异常并终止运行。只有系统平台签名或者系统应用才会授予这个权限,这也是第三方应用无法实现静默安装的原因。

        final int callingUid = Binder.getCallingUid();
	    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            installFlags |= PackageManager.INSTALL_FROM_ADB;
        } else {
            // Caller holds INSTALL_PACKAGES permission, so we're less strict
            // about installerPackageName.
            installFlags &= ~PackageManager.INSTALL_FROM_ADB;
            installFlags &= ~PackageManager.INSTALL_ALL_USERS;
        }

第二步检查callingUid是否等于SHELL或者ROOT,如果是表示通过adb shell命令发起的安装动作,给installFlags添加对应INSTALL_FROM_ADB标志,否则去掉。这个标志后面会用到。

	verificationParams.setInstallerUid(callingUid);
        final File originFile = new File(originPath);
        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, observer, installFlags,
                installerPackageName, verificationParams, user, packageAbiOverride);
        mHandler.sendMessage(msg);
}

最后创建一个whatINIT_COPYMessage把调用的参数保存在InstallParams对象中并发送到mHandler的消息队列中。

下面分析下INIT_COPY类型消息的处理:

    class PackageHandler extends Handler {
        private boolean mBound = false;
        final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>();

        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);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }

INIT_COPY消息的处理中将绑定DefaultContainerService,因为这是一个异步的过程,要等待绑定的结果通过onServiceConnected()返回,所以,这里将安装的参数信息放到mPendingInstalls列表中。如果这个service以前就绑定好了,现在不在需要再绑定,安装信息也会先放到mPendingInstalls中。如果有多个安装请求同时到达,通过mPendingInstalls列表可以对它们进行排队。如果列表中只有一项,说明没有更多的安装请求,此时会立即发送MCS_BOUND消息,进入下一步的处理。而onServiceConnected()方法的处理同样是发送MCS_BOUND消息,onServiceConnected方法如下:

    class DefaultContainerConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
        }
        public void onServiceDisconnected(ComponentName name) {
        }
    };

一旦DefaultContainerService(该服务用于外部存储卡应用安装拷贝用)服务绑定成功后,通过mHandlerMCS_BOUND消息,下面来分析下这个消息的处理过程:

                case MCS_BOUND: {
                    if (msg.obj != null) {
                        mContainerService = (IMediaContainerService) msg.obj;
                    }
                    if (mPendingInstalls.size() > 0) {
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            if (params.startCopy()) {
                                // Delete pending install
                                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);
                                }
                            }
                        }
                    }
                }

MCS_BOUND消息的处理过程就是调用InstallParams类的startCopy()方法来执行安装操作,一次安装完成后在待安装列表中删除。只要mPendingInstalls中还有安装信息,就会重复发送MCS_BOUND消息,直到所有的应用都安装完毕,最后发送一个延时10秒的MCS_UNBIND消息。MCS_UNBIND消息不细分析。

安装的开始在InstallParams类的startCopy方法,该方法定义在InstallParams的父类HandlerParams中:

    private abstract class HandlerParams {
        private static final int MAX_RETRIES = 4;
        …
        final boolean startCopy() {
            boolean res;
            try {           
                    handleStartCopy();
                    res = true;                
            }
            handleReturnCode();
            return res;
        }

父类的startCopy方法最后调用handleStartCopy方法,其中抽象的父类只声明了handleStartCopy抽象方法,具体实现在子类InstallParams中:

    class InstallParams extends HandlerParams {
        final OriginInfo origin;
        final IPackageInstallObserver2 observer;
        int installFlags;
        final String installerPackageName;
        final VerificationParams verificationParams;
        private InstallArgs mArgs;
        private int mRet;
        final String packageAbiOverride;

       public void handleStartCopy() throws RemoteException {
            int ret = PackageManager.INSTALL_SUCCEEDED;
	        …
            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
            PackageInfoLite pkgLite = null;        
            …
            final InstallArgs args = createInstallArgs(this);
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
            …
                    /*
                     * No package verification is enabled, so immediately start
                     * the remote call to initiate copy using temporary file.
                     */
                    ret = args.copyApk(mContainerService, true);
                
            }
            mRet = ret;
        }

如果忽略handleStartCopy中一堆繁杂的检验判断,最终调用InstallArgscopyApk方法,传入ContainerServicebinder实例。另外真正的copyApk方法在其子类FileInstallArgscopyApk方法中实现:

    class FileInstallArgs extends InstallArgs {
        private File codeFile;
        private File resourceFile;
        private File legacyNativeLibraryPath;

       int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            …
            int ret = PackageManager.INSTALL_SUCCEEDED;
            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                return ret;
            }

            final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            try {
                handle = NativeLibraryHelper.Handle.create(codeFile);
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        abiOverride);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
            return ret;
        }

发现copyApk方法同样是调用DefaultContainerServicecopyPackage方法将应用的文件复制到/data/app下。如果应用中还有native的动态库,也会把包在apk文件中的动态库的文件提取出来。执行完copyApk后,安装的第一阶段工作就完成了,应用安装到了/data/app目录下。

第一阶段(复制apk文件)完成后,继续完成第二阶段的扫描安装工作:

在前面的startCopy()方法中已经看到,最后它会调用handleReturnCode()方法,其实现在InstallParams类中:

        void handleReturnCode() {
            if (mArgs != null) {
                processPendingInstall(mArgs, mRet);
            }
        }

接下来分析processPendingInstall方法的实现:

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                 // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.returnCode = currentStatus;
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = new PackageRemovedInfo();
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        installPackageLI(args, res);
                    }
                    args.doPostInstall(res.returnCode, res.uid);
                }
        });
    }

processPendingInstall方法中post了一个消息,这样安装过程将以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI来装载应用,接下来的一大段代码都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过发送POST_INSTALL消息来继续处理。现在来分析下apk的装载过程:

   private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        final int installFlags = args.installFlags;
        String installerPackageName = args.installerPackageName;
        File tmpPackageFile = new File(args.getCodePath());
        boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
        boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);
        boolean replace = false;
        final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
        // Result object to be returned
        res.returnCode = PackageManager.INSTALL_SUCCEEDED;
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setDisplayMetrics(mMetrics);
        final PackageParser.Package pkg;
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        // Mark that we have an install time CPU ABI override.
        pkg.cpuAbiOverride = args.abiOverride;
        String pkgName = res.name = pkg.packageName;  
        pp.collectCertificates(pkg, parseFlags);
        pp.collectManifestDigest(pkg);
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // Check if installing already existing package
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                String oldName = mSettings.mRenamedPackages.get(pkgName);
                if (pkg.mOriginalPackages != null  && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
                    replace = true;
                } else if (mPackages.containsKey(pkgName)) {
                    // This package, under its official name, already exists
                    // on the device; we should replace it.
                    replace = true;
                }
            }
            PackageSetting ps = mSettings.mPackages.get(pkgName);
                if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {
                    try {
                        verifySignaturesLP(ps, pkg);
                    } catch (PackageManagerException e) {
                        res.setError(e.error, e.getMessage());
                        return;
                    }
                } else {
                    if (!checkUpgradeKeySetLP(ps, pkg)) {
                        return;
                    }
                }
            int N = pkg.permissions.size();
            for (int i = N-1; i >= 0; i--) {
                PackageParser.Permission perm = pkg.permissions.get(i);
                BasePermission bp = mSettings.mPermissions.get(perm.info.name);
                if (bp != null) {
                    final boolean sigsOk;
                    if (!bp.sourcePackage.equals(pkg.packageName)
                            || !(bp.packageSetting instanceof PackageSetting)
                            || !bp.packageSetting.keySetData.isUsingUpgradeKeySets()
                            || ((PackageSetting) bp.packageSetting).sharedUser != null) {
                        sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
                    } else {
                        sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
                    }
                }
            }
        }
        if (replace) {
            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, res);
        } else {
            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, res);
        }
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }
        }
    }

installPackageLI方法首先解析了安装的应用文件,得到解析的结果后,主要是判断新安装的应用是否和系统中已安装的应用构成升级关系,如果是,调用replacePackageLI方法继续处理,否则调用installNewPackageLI方法处理。





猜你喜欢

转载自blog.csdn.net/lgglkk/article/details/54917852