APK的安装流程及PackageManagerService源码解析&静默安装

Android的应用管理主要是通过PackageManagerService来完成的,PackageManagerService服务负责各种APK包的安装,卸载,优化和查询.

PackageManagerService在系统启动时会扫描所有APK文件和jar包,然后把它们的信息读取出来,保存在内存中,这样系统运行时就能迅速找到各种应用和组件的信息.扫描过程中如果遇到没有优化的文件,还要执行转换工作,将APP文件中的dex格式转换成oat格式(Android5.0之前是转换成odex格式);启动后,将提供安装包的信息查询服务及应用的安装和卸载服务.

一、安装涉及的目录介绍:

system/priv-app —- 存放一些系统底层应用(Settings,SystemUI等,拥有的权限最高)

system/app —- 存放系统级的应用(phone,Contacts等)

data/app —- 存放用户安装的应用

data/data —- 存放安装应用程序的数据

data/dalvik-cache —- 存放大部分的apk文件和jar包的odex版本,odex是一种优化过的格式,执行速度比apk文件中的dex格式更快,如果系统运行在art模式下,这里保存的是oat格式的文件,这种文件格式是Linux的ELF格式的一种私有形式

data/system/packages.xml —- 记录系统中所有安装的应用信息,包括基本信息、签名和权限

data/system/packages.list —- 保存普通应用的数据目录和uid等信息

二、apk安装的方式:

  • 1.安装系统APK和预置的APK(第一次开机时安装,没有安装界)

PackageManagerService的构造中会扫描对应目录下的apk,完成安装

  • 2.网络下载应用安装――通过market应用完成,没有安装界面

调用PackageManager的installPackage方法执行安装

  • 3.ADB工具安装,没有安装界面

调用:msm8976/repo/system/core/adb/commandline.cpp中install_app方法,该方法调用pm_command通过send_shell_command方法将数据发送到手机端的adbd守护进程中,adbd在收到PC中Console发来的数据之后启动一个Shell,然后执行pm脚本(pm位于/system/bin目录下).

pm脚本通过app_process执行pm.jar包的main函数(\system\framework\pm.jar)
对应源码: msm8976/repo/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java

private int runInstall() {
        ...
        ...
        LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
        try {
            VerificationParams verificationParams = new VerificationParams(verificationURI,
                    originatingURI, referrerURI, VerificationParams.NO_UID, null);

            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                    installerPackageName, verificationParams, abi, userId);

            synchronized (obs) {
                while (!obs.finished) {
                    try {
                        obs.wait();
                    } catch (InterruptedException e) {
                    }
                }
                if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
                    System.out.println("Success");
                    return 0;
                } else {
                    System.err.println("Failure ["
                            + installFailureToString(obs)
                            + "]");
                    return 1;
                }
            }
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
    }

调用PMS的installPackageAsUser完成安装

  • 4.第三方应用安装,有安装界面

安装本地apk,有安装界面,由package/app/PackageInstaller应用处理安装及卸载过程的界面

以上方式都有PackageInstallObserver来监听安装是否成功

三、APK安装流程:

复制APK安装到data/app目录下;解析APK信息;解压安装包的dex文件,执行dexopt转换操作,把转换过的文件保存到dalvik-cache目录,更新apk安装后的一些信息完成安装

安装应用后,会在/data/system/packages.xml文件中写入app的信息.同理删除某个app时,也会从中删除该app的信息.尤其是内置应用,更需要将app的残留删除干净,不然再次安装app会报错:INSTALL_FAILED_UPDATE_INCOMPATIBLE

在应用中,我们通常是调用Context的getPackageManager()方法返回PackageManager对象,源码如下:

// msm8976/repo/frameworks/base/core/java/android/app/ContextImpl.java
    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }
        return null;
    }

这里返回的实际是ApplicationPackageManager对象,这个对象创建时使用IPackageManager对象作为参数,IPackageManager是PackageManagerService实现的AIDL接口,通过binder来获取PackageManagerService对象,源码如下:

// msm8976/repo/frameworks/base/core/java/android/app/ActivityThread.java

public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
}

因此ApplicationPackageManager是PackageManagerService的代理对象.ApplicationPackageManager继承自PackageManager,PackageManager中定义了可以操作PackageManagerService的接口.

在PackageManager中,实现对apk安装的方法是installExistingPackage和installPackageWithVerificationAndEncryption(前者是通过包名去升级应用,后者是安装新的应用),对应源码如下:

// msm8976/repo/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
public void initView() {
            ....
    if ("package".equals(mPackageURI.getScheme())) {
            try {
                pm.installExistingPackage(mAppInfo.packageName);
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_SUCCEEDED);
            } catch (PackageManager.NameNotFoundException e) {
                observer.packageInstalled(mAppInfo.packageName,
                        PackageManager.INSTALL_FAILED_INVALID_APK);
            }
        } else {
            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
                    installerPackageName, verificationParams, null);
    }
}

最终调用到PackageManagerService中的installExistingPackageAsUser和installPackage方法继续安装逻辑(这也就是传说中静默安装需要调用的方法)

PackageManagerService是处理应用的安装、卸载、管理等工作,开机时由systemServer启动.

PackageManagerService的构造中会扫描对应目录下的apk执行安装内置apk,同时会初始化两个重要的成员变量mInstaller和mInstallerService,mInstallerService是PackageInstallerService的实例,主要来管理安装会话,mInstaller是类Installer的实例,这个类比较简单,它有一个mInstaller的成员变量,这个变量是InstallerConnection的实例.InstallerConnection类中存在与守护进程Install通讯的Socket命令通道,实际上系统中进行APK文件格式转换等工作最后是由InstallID进程来完成;InstallerConnection中通过socket在系统installd进程中完成安装

//源码路径: msm8976/repo/frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
private boolean connect() {
        if (mSocket != null) {
            return true;
        }
        Slog.i(TAG, "connecting...");
        try {
            mSocket = new LocalSocket();

            LocalSocketAddress address = new LocalSocketAddress("installd",
                    LocalSocketAddress.Namespace.RESERVED);
            mSocket.connect(address);

            mIn = mSocket.getInputStream();
            mOut = mSocket.getOutputStream();
        } catch (IOException ex) {
            disconnect();
            return false;
        }
        return true;
    }

为什么需要installID进程?因为PMS运行在system_server中,是一个System用户,system用户并没有访问应用程序目录的权限,而InstallID的作用是处理root权限的操作.

installd源码路径:msm8976/repo/frameworks/native/cmds/installd

继续安装逻辑,PackageManagerService中installPackage会调用installPackageAsUser方法,该方法首先调用enforceCallingOrSelfPermission判断调用安装的程序是否申请了安装权限(所以要实现静默安装也需要申请该权限).这里判断安装来源,然后通过mHandler发送INIT_COPY的消息,这个mHandler运行在一个HandlerThread中

@Override
    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();
        enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");

        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            try {
                if (observer != null) {
                    observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
                }
            } catch (RemoteException re) {
            }
            return;
        }

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

        UserHandle user;
        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }

        // Only system components can circumvent runtime permissions when installing.
        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);

        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
                null, verificationParams, user, packageAbiOverride, null);
        mHandler.sendMessage(msg);
    }

INIT_COPY主要是确保DefaultContainerService已bound,DefaultContainerService是一个应用服务,负责实现APK等相关资源文件在内部或外部存储器上的存储工作.而MCS_BOUND中则执行了
params.startCopy(),将apk文件复制到data/app目录下.复制的关键代码如下:

        /*
         * Invoke remote method to get package information and install
         * location values. Override install location based on default
         * policy if needed and then create install arguments based
         * on the install location.
         */
        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;

            if (onInt && onSd) {
                // Check if both bits are set.
                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else {
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                        packageAbiOverride);

                /*
                 * If we have too little free space, try to free cache
                 * before giving up.
                 */
                if (!origin.staged && pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                    // TODO: focus freeing disk space on the target device
                    final StorageManager storage = StorageManager.from(mContext);
                    final long lowThreshold = storage.getStorageLowBytes(
                            Environment.getDataDirectory());

                    final long sizeBytes = mContainerService.calculateInstalledSize(
                            origin.resolvedPath, isForwardLocked(), packageAbiOverride);

                    if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
                        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                installFlags, packageAbiOverride);
                    }

                    /*
                     * The cache free must have deleted the file we
                     * downloaded to install.
                     *
                     * TODO: fix the "freeCache" call to not delete
                     *       the file we care about.
                     */
                    if (pkgLite.recommendedInstallLocation
                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                        pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                    }
                }
            }
            ...
            ...
            final InstallArgs args = createInstallArgs(this);
            mArgs = args;

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 /*
                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
                 */
                int userIdentifier = getUser().getIdentifier();
                if (userIdentifier == UserHandle.USER_ALL
                        && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
                    userIdentifier = UserHandle.USER_OWNER;
                }

                /*
                 * Determine if we have any installed package verifiers. If we
                 * do, then we'll defer to them to verify the packages.
                 */
                final int requiredUid = mRequiredVerifierPackage == null ? -1
                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                ...
                {
                    /*
                     * 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的核心是copyApk,其它是一些安全校验(存储空间检查,权限检查等)

startCopy方法走完copy apk的逻辑之后来到了handleReturnCode方法,然后调用processPendingInstall方法,这个方法调用installPackageLI来执行解析package等操作之后通过mHandler发送一个POST_INSTALL消息完成安装(发送Intent.ACTION_PACKAGE_ADDED广播)

installPackageLI首先解析package包,然后做大量签名和权限校验工作,该方法又会调用到installNewPackageLI,这个方法主要做了两件事: scanPackageLI负责安装,updateSettingLI完成安装后的设置信息更新

scanPackageLI调用scanPackageDirtyLI实现具体的逻辑,摘取该方法中一段

if ((scanFlags & SCAN_NO_DEX) == 0) {
            int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
                    forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
                    (scanFlags & SCAN_BOOTING) == 0);
            if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
            }
        }

执行mPackageDexOptimizer.performDexOpt(x,x,x)方法,继续跟代码最后调用到performDexOptLI方法执行dexopt操作,同PMS构造方法中一样,通过socket与installd进程通讯完成转换工作

Apk文件其实是一个压缩包,我们的代码最终都编译成了.dex文件,但为了提高运行性能,android系统并不会直接执行.dex,而是在安装过程中执行dexopt操作来优化.dex文件,最终系统运行的是优化后的’odex’文件(odex文件的后缀也是.dex,路径在data/dalvik-cache).对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,将.dex文件翻译成oat文件.

以上源码基于Android6.0分析
对应流程图:
《APK的安装流程及PackageManagerService源码解析&静默安装》

    原文作者:Ansen360
    原文地址: https://blog.csdn.net/qq_25804863/article/details/48565569
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞