首页 > 学院 > 开发设计 > 正文

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

2019-11-09 17:27:39
字体:
来源:转载
供稿:网友

系统开机过程中扫描并安装apk的过程是针对已有的apk文件。针对外部apk的安装,pms提供了另外的接口。我们一般也就通过两种方式去安装外部apk,一种是通过adb的install命令安装外部应用。另外一种是通过系统应用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方法最终通过binder和pms通信,执行pms的installPackageAsUser方法,分别传入apk的文件路径,abi信息等。pms的installPackageAsUser方法在接下来会分析。

通过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文件路径,最后调用pms的installPackage方法(实际上同样是调用installPackageAsUser)执行具体的安装操作。

installPackageAsUser方法分析

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

[/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);}

最后创建一个what是INIT_COPY的Message,把调用的参数保存在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(该服务用于外部存储卡应用安装拷贝用)服务绑定成功后,通过mHandler发MCS_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中一堆繁杂的检验判断,最终调用InstallArgs的copyApk方法,传入ContainerService的binder实例。另外真正的copyApk方法在其子类FileInstallArgs的copyApk方法中实现:

    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方法同样是调用DefaultContainerService的copyPackage方法将应用的文件复制到/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方法处理。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表