开篇

核心源码

前言

在本系列上一篇文章 【深入研究 PackageManagerService 系列(5)之 PackageInstaller - APK 安装流程】 中,我们了解了 PackageInstaller 安装 APK 的流程,最后会将 APK 的信息交由 PMS 处理。那么 PMS 是如何处理的?这就是我们这篇文章需要分析的。

PackageHandler

commitLocked

在前一篇文章末尾,我们讲过 commitLocked 方法,我们回顾下:

    private final PackageManagerService mPm;

    private void commitLocked() throws PackageManagerException {
        ... ...

        /**
         * commitLocked 方法很长,我们主要关注这一行代码
         * 调用 PackageManagerService 的 installStage 方法
         * 这样安装 APK 的代码逻辑就进入了 PackageManagerService 中
         */
        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                mInstallerPackageName, mInstallerUid, user, mCertificates);
    }

installStage

正式进入 PMS 源码分析流程,我们看看 installStage 方法:

    void installStage(String packageName, File stagedDir, String stagedCid,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            Certificate[][] certificates) {
        ... ...

        // 创建类型为 INIT_COPY 的消息
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final int installReason = fixUpInstallReason(installerPackageName, installerUid,
                sessionParams.installReason);
        // 创建 InstallParams,它对应于包的安装数据
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, certificates, installReason);
        params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
        msg.obj = params;
        ... ...

        // 将 InstallParams 通过消息发送出去
        mHandler.sendMessage(msg);
    }

handleMessage

因为 PackageHandler 继承 Handler ,所以我们来看下 PackageHandler 的 HandlerMessage 方法:

        public void handleMessage(Message msg) {
            try {
                doHandleMessage(msg);
            } finally {
                // 设置了线程的优先级为后台线程
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            }
        }

INIT_COPY

接下来看下 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: {
                    // 取出 InstallParams
                    HandlerParams params = (HandlerParams) msg.obj;
                    // idx 为当前需要安装的 APK 个数,mPendingInstalls 里面保存所有需要安装的 APK 解析出来的 HandlerParams 参数
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);

                    // mBound 用于标识是否绑定了服务(DefaultContainerService),
                    // 如果已经绑定了,则 mBound 为true,如果是第一次调用 mBound 为 false,默认值为 false。
                    if (!mBound) {
                        Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                System.identityHashCode(mHandler));
                        // 如果没有绑定服务,重新绑定,connectToService 方法内部如果绑定成功会将 mBound 置为 true
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                    System.identityHashCode(mHandler));
                            if (params.traceMethod != null) {
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
                                        params.traceCookie);
                            }
                            // 绑定服务失败则 return
                            return;
                        } else {
                            // 绑定服务成功,将请求添加到 ArrayList 类型的 mPendingInstalls 中,等待处理
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        // 已经绑定服务
                        mPendingInstalls.add(idx, params);
                        if (idx == 0) {
                            // 如果是第一个安装请求,则直接发送事件 MCS_BOUND 触发处理流程
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }
        ... ...
    }

connectToService

假设我们是第一次走流程,还没有绑定服务,则会调用 connectToService() 方法,我们看下流程:

    class PackageHandler extends Handler {
        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);
            /**
             * bindServiceAsUser 方法会传入 mDefContainerConn,
             * bindServiceAsUser 方法的处理逻辑和我们调用 bindService 是类似的,
             * 服务建立连接后,会调用 onServiceConnected
             */
            if (mContext.bindServiceAsUser(service, mDefContainerConn,
                    Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 如果绑定 DefaultContainerService 成功,mBound 会置为 ture
                mBound = true;
                return true;
            }
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return false;
        }

这里可以看到 bind 到了一个 service ,这个 service 的 ComponentName 是 "DEFAULT_CONTAINER_COMPONENT" 这个常量,那我们就来看下这个 ComponentName。

    static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
            DEFAULT_CONTAINER_PACKAGE,
            "com.android.defcontainer.DefaultContainerService");

所以我们知道 bind 的 service 是 DefaultContainerService 。绑定 DefaultContainerService 之后,设定进程的优先级为 THREAD_PRIORITY_DEFAULT。

然后等 bindServiceAsUser 这个方法执行完则又把线程的优先级设为 THREAD_PRIORITY_BACKGROUND。

我们这边要重点提到一个 mDefContainerConn 变量,研究一下:

    final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();

这下我们知道了 mDefContainerConn 的类型是 DefaultContainerConnection ,那我们来看下 DefaultContainerConnection 这个类。

DefaultContainerConnection

    // DefaultContainerConnection 实现了 ServiceConnection,所以在连接成功的时候会调用 onServiceConnected 方法
    class DefaultContainerConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
            final IMediaContainerService imcs = IMediaContainerService.Stub
                    .asInterface(Binder.allowBlocking(service));
            // 发送了 MCS_BOUND 类型的消息
            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
        }

        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
        }
    }
上文我们提及到 mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM) 方法,
其实就是"绑定" DefaultContainerService。我们知道 bind 一个 Service ,其中负责通信的 ServiceConnection,
而本方法中负责通信的就是 mDefContainerConn。所以一旦绑定成功会执行 mDefContainerConn 的 onServiceConnected 方法。
而现实是当绑定成功后在 onServiceConnected 中将一个 IBinder 转换成了一个 IMediaContainerService。
这个就是 onServiceConnected 回调函数中根据参数传进来的 IMediaContainerService.Stub 的对象引用创建的一个远程代理对象,
后面 PacakgeManagerServic 通过该代理对象访问 DefaultContainerService 服务。

我们简单梳理一下以上代码所做的工作:

        ✨  mBound 用于标识是否绑定了 DefaultContainerService,默认值为 false。

        ✨  DefaultContainerService 是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此 DefaultContainerService 没有和 PMS 运行在同一进程中,它运行在 com.android.defcontainer 进程,通过 IMediaContainerService 和 PMS 进行 IPC 通信。

彼此之间的 IPC 通信如下图所示:
Android 8.1 源码_核心篇 -- 深入研究 PMS 系列(6)之 APK 安装流程(PMS)-LMLPHP

        ✨  connectToService 方法用来绑定 DefaultContainerService。

        ✨  mHandler.sendEmptyMessage(MCS_BOUND):发送 MCS_BOUND 类型的消息,触发处理第一个安装请求。

不知道你是否发现,有两个发送 MCS_BOUND 类型消息的方法:

// PackageHandler.doHandleMessage(已绑定服务)
mHandler.sendEmptyMessage(MCS_BOUND);     // 不带参数

// DefaultContainerConnection(未绑定服务 - 绑定服务)
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));     // 带参数

MCS_BOUND 分析

不带参数

case MCS_BOUND: {
    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
    // 不带参数,则此条件不满足
    if (msg.obj != null) {
        mContainerService = (IMediaContainerService) msg.obj;
        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
        System.identityHashCode(mHandler));
    }

    // 走这边的逻辑
    if (mContainerService == null) {
        // 服务没有绑定,则走这边,但是之前我们讲解过,发送 MCS_BOUND 时,已经绑定了服务,这显然是不正常的
        if (!mBound) {
            // Something seriously wrong since we are not bound and we are not
            // waiting for connection. Bail out.
            Slog.e(TAG, "Cannot bind to media container service");
            for (HandlerParams params : mPendingInstalls) {
                // 负责处理服务发生错误的情况
                params.serviceError();
                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                        System.identityHashCode(params));
                if (params.traceMethod != null) {
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
                            params.traceMethod, params.traceCookie);
                }
                return;
            }
            // 绑定失败,清空安装请求队列
            mPendingInstalls.clear();
        } else {
            // 继续等待绑定服务
            Slog.w(TAG, "Waiting to connect to media container service");
        }
    } else if (mPendingInstalls.size() > 0) {
        ... ...
    } else {
        // Should never happen ideally.
        Slog.w(TAG, "Empty queue");
    }
    break;
}

带参数

case MCS_BOUND: {
    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
    if (msg.obj != null) {
        mContainerService = (IMediaContainerService) msg.obj;
        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                System.identityHashCode(mHandler));
    }
    // 带参数,此条件不满足
    if (mContainerService == null) {
        ... ...
        // 走这边的逻辑,安装请求队列不为空
    } else if (mPendingInstalls.size() > 0) {
        // 得到安装请求队列第一个请求 HandlerParams
        HandlerParams params = mPendingInstalls.get(0);
        if (params != null) {
            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params));
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
            // 如果 HandlerParams 不为 null 就会调用 HandlerParams 的 startCopy 方法,用于开始复制 APK 的流程
            if (params.startCopy()) {
                if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind...");
                // 如果 APK 安装成功,删除本次安装请求
                if (mPendingInstalls.size() > 0) {
                    mPendingInstalls.remove(0);
                }
                if (mPendingInstalls.size() == 0) {
                    if (mBound) {
                        // 如果没有安装请求了,发送解绑服务的请求
                        if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND");
                        removeMessages(MCS_UNBIND);
                        Message ubmsg = obtainMessage(MCS_UNBIND);
                        sendMessageDelayed(ubmsg, 10000);
                    }
                } else {
                    if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work");
                    // 如果还有其他的安装请求,接着发送 MCS_BOUND 消息继续处理剩余的安装请求
                    mHandler.sendEmptyMessage(MCS_BOUND);
                }
            }
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    // 如果安装请求数不大于 0 就会打印 “Empty queue”
    } else {
        // Should never happen ideally.
        Slog.w(TAG, "Empty queue");
    }
    break;
}

上面的流程其实很简单,我们根据是否传入了 ims 这个参数,走两条流程,核心的方法就是最终的 startCopy()。

复制 APK

上面我们提过,Copy APK 的操作是调用 HandlerParams 的 startCopy 方法。HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。

startCopy

    private abstract class HandlerParams {
        private static final int MAX_RETRIES = 4;

        /**
         * Number of times startCopy() has been attempted and had a non-fatal
         * error.
         */
        private int mRetries = 0;
        ... ...

        final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                /**
                 * mRetries 用于记录 startCopy 方法调用的次数,调用 startCopy 方法时会先自动加 1
                 * startCopy 方法尝试的次数超过了 4 次,就放弃这个安装请求
                 */
                if (++mRetries > MAX_RETRIES) {
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    // 发送 MCS_GIVE_UP 类型消息,将第一个安装请求(本次安装请求)从安装请求队列 mPendingInstalls 中移除掉
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    handleStartCopy();       // 💥 💥 💥 重点方法 💥 💥 💥
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            // 调用 handleReturnCode 抽象方法,这个方法会在 handleStartCopy 执行完拷贝相关行为之后,根据 handleStartCopy 做进一步的处理,主要返回状态码
            handleReturnCode();
            return res;
        }
        ... ...

        abstract void handleStartCopy() throws RemoteException;
        abstract void handleServiceError();
        abstract void handleReturnCode();
    }

这边我们还是简单的看一下 MCS_GIVE_UP 和 MCS_RECONNECT 两种 message 的处理流程,逻辑相当简单:

MCS_RECONNECT

                case MCS_RECONNECT: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
                    if (mPendingInstalls.size() > 0) {
                        if (mBound) {
                            disconnectService();
                        }
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            for (HandlerParams params : mPendingInstalls) {
                                // Indicate service bind error
                                params.serviceError();
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                        System.identityHashCode(params));
                            }
                            mPendingInstalls.clear();
                        }
                    }
                    break;
                }

判断安装请求队列 mPendingInstalls 是否还有元素,如果有元素先断开绑定,则再次重新调用 connectToService 方法,我们知道 connectToService() 内部会再次执行绑定 DefaultContainerService,而在绑定成功后会再次发送一个 what 值为 MCS_BOUND 的 Message,从而又回到了 startCopy 里面。

MCS_GIVE_UP

                case MCS_GIVE_UP: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
                    HandlerParams params = mPendingInstalls.remove(0);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    break;
                }

直接删除了安装请求队列 mPendingInstalls 里面下标为 0 的元素,即取消本次安装请求。

handleStartCopy

我们发现 handleStartCopy 也是一个抽象的方法,那么它在哪实现?前面我们说过:HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。

    class InstallParams extends HandlerParams {
        /*
         * 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;
            ... ...

            /**
             * 确定 APK 的安装位置
             *     onSd:     安装到 SD 卡
             *     onInt:    内部存储即 Data 分区
             *     ephemeral:安装到临时存储
             */
            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
            final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;

            PackageInfoLite pkgLite = null;

            // APK 不能同时安装在 SD 卡和 Data 分区
            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;
            // 安装标志冲突,Instant Apps 不能安装到 SD 卡中
            } else if (onSd && ephemeral) {
                Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else {
                /**
                 * 获取 APK 的少量的信息
                 * 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 getMinimalPackageInfo 方法,
                 * 该方法轻量解析 APK 并得到 APK 的少量信息,
                 * 轻量解析的原因是这里不需要得到 APK 的全部信息,APK 的少量信息会封装到 PackageInfoLite 中。
                 */
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                        packageAbiOverride);

                if (DEBUG_EPHEMERAL && ephemeral) {
                    Slog.v(TAG, "pkgLite for install: " + pkgLite);
                }
                ... ..
            }
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                // 判断安装的位置
                int loc = pkgLite.recommendedInstallLocation;
                if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                    ... ...
                } else {
                    installFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite,
                            mSettings.mPackages, getUser());
                    loc = installLocationPolicy(pkgLite);
                    ... ...
                }
            }

            /**
             * 根据 InstallParams 创建 InstallArgs 对象
             * InstallArgs 是一个抽象类,定义了 APK 的安装逻辑,比如"复制"和"重命名" APK 等
             *
             *     abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
             *
             * InstallArgs 有 3 个子类,都被定义在 PMS 中:
             *     FileInstallArgs:用于处理安装到非 ASEC 的存储空间的 APK ,也就是内部存储空间(Data分区)
             *     AsecInstallArgs:用于处理安装到 ASEC 中(mnt/asec)即 SD 卡中的 APK
             *     MoveInstallArgs:用于处理已安装 APK 的移动的逻辑
             */
            final InstallArgs args = createInstallArgs(this);
            mArgs = args;

            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                ... ...

                if (!origin.existing && requiredUid != -1
                        && isVerificationEnabled(
                                verifierUser.getIdentifier(), installFlags, installerUid)) {
                    ... ...
                } else {
                    // 对 APK 进行检查后就会调用 InstallArgs 的 copyApk 方法进行安装
                    ret = args.copyApk(mContainerService, true);
                }
            }

            mRet = ret;
        }
        ... ...
    }

FileInstallArgs

OK,我们知道 InstallParams 有三个子类,不同的 InstallArgs 子类会有着不同的处理,那我们现在以 FileInstallArgs 为例跟踪学习一下具体的流程:

copyApk

    /**
     * Logic to handle installation of non-ASEC applications, including copying
     * and renaming logic.
     */
    class FileInstallArgs extends InstallArgs {
        private File codeFile;
        ... ...

        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
            try {
                return doCopyApk(imcs, temp);
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }

doCopyApk

调用了 doCopyApk 方法:

        private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            ... ...

            try {
                final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
                // 用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp ,其中 18300388 是安装的 sessionId
                final File tempDir =
                        mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }
            ... ...

            int ret = PackageManager.INSTALL_SUCCEEDED;
            /**
             * 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 copyPackage 方法,
             * 这个方法会在 DefaultContainerService 所在的进程中将 APK 复制到临时存储目录,
             * 比如 /data/app/vmdl18300388.tmp/base.apk 。
             */
            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);    // 真正的文件拷贝
            ... ...

            return ret;
        }

安装 APK

handleReturnCode

我们回到 APK 的复制调用链的头部方法:HandlerParams 的 startCopy 方法,在最后 调用了 handleReturnCode 方法,进行 APK 的安装。

    private abstract class HandlerParams {
        private static final int MAX_RETRIES = 4;

        private int mRetries = 0;
        ... ...

        final boolean startCopy() {
            boolean res;
            try {
                if (++mRetries > MAX_RETRIES) {
                    ... ...
                } else {
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                ... ...
            }
            // 处理复制 APK 后的安装 APK 逻辑
            handleReturnCode();       // 💥 💥 💥 💥 💥 💥
            return res;
        }
        ... ...

        abstract void handleReturnCode();
    }

handleReturnCode 也是一个抽象方法,那么在哪里实现?同样,它的实现在 InstallParams 中。

        @Override
        void handleReturnCode() {
            // If mArgs is null, then MCS couldn't be reached. When it
            // reconnects, it will try again to install. At that point, this
            // will succeed.
            if (mArgs != null) {
                // "装载代码"的入口是 processPendingInstall(InstallArgs,int) 方法
                processPendingInstall(mArgs, mRet);
            }
        }

我们发现调用了 processPendingInstall 方法,继续跟!

processPendingInstall

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.setReturnCode(currentStatus);
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = null;
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    /**
                     * 安装前处理
                     * 用于检查 APK 的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的 APK 文件
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                        installPackageTracedLI(args, res);       // 💥 💥 💥 💥 💥 💥
                    }
                    /**
                     * 安装后收尾
                     * 用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件
                    args.doPostInstall(res.returnCode, res.uid);
                }
                ... ...
            }
        });
    }

installPackageTracedLI

    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
            installPackageLI(args, res);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

installPackageLI

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ... ...

        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setDisplayMetrics(mMetrics);
        pp.setCallback(mPackageParserCallback);

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
        final PackageParser.Package pkg;
        try {
            // 解析 APK
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        ... ...

        // Get rid of all references to package scan path via parser.
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // 检查 APK 是否存在
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                // 获取没被改名前的包名
                String oldName = mSettings.getRenamedPackageLPr(pkgName);
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
                    // 设置标志位表示是替换安装
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
                            + oldName + " pkgName=" + pkgName);
                }
                ... ...
            }

            PackageSetting ps = mSettings.mPackages.get(pkgName);
            // 查看 Settings 中是否存有要安装的 APK 的信息,如果有就获取签名信息
            if (ps != null) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

                PackageSetting signatureCheckPs = ps;
                if (pkg.applicationInfo.isStaticSharedLibrary()) {
                    SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
                    if (libraryEntry != null) {
                        signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
                    }
                }

                // 检查签名的正确性
                if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
                    if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
                        res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                + pkg.packageName + " upgrade keys do not match the "
                                + "previously installed version");
                        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 (systemApp || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) {
            if (onExternal) {
                // 系统APP不能在SD卡上替换安装
                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                        "Cannot install updates to system apps on sdcard");
                return;
            } else if (instantApp) {
                // 系统 APP 不能被 Instant App 替换
                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
                        "Cannot update a system app with an instant app");
                return;
            }
        }

        ... ...

        // 重命名临时文件
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }

        if (!instantApp) {
            startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
        } else {
            if (DEBUG_DOMAIN_VERIFICATION) {
                Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
            }
        }

        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
            if (replace) {
                // 替换安装
                if (pkg.applicationInfo.isStaticSharedLibrary()) {
                    PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
                    if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
                        res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                                + "static-shared libs cannot be updated");
                        return;
                    }
                }
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res, args.installReason);
            } else {
                // 安装新的 APK
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res, args.installReason);
            }
        }

        // 更新应用程序所属的用户
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
                ps.setUpdateAvailable(false /*updateAvailable*/);
            }
            ... ...
        }
    }

installPackageLI 方法的代码很长,这里截取主要的部分,主要做了几件事:

        ✨ 1、创建 PackageParser 解析 APK 。
        ✨ 2、检查 APK 是否存在,如果存在就获取此前没被改名前的包名,赋值给 PackageParser.Package 类型的 pkg ,将标志位 replace 置为 true 表示是替换安装。
        ✨ 3、如果 Settings 中保存有要安装的 APK 的信息,说明此前安装过该 APK ,则需要校验 APK 的签名信息,确保安全的进行替换。
        ✨ 4、将临时文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名为 /data/app/包名-1/base.apk 。这个新命名的包名会带上一个数字后缀 1,每次升级一个已有的 App ,这个数字会不断的累加。
        ✨ 5、系统 APP 的更新安装会有两个限制,一个是系统 APP 不能在 SD 卡上替换安装,另一个是系统 APP 不能被 Instant App 替换。
        ✨ 6、根据 replace 来做区分,如果是替换安装就会调用 replacePackageLIF 方法,其方法内部还会对系统 APP 和非系统 APP 进行区分处理,如果是新安装 APK 会调用 installNewPackageLIF 方法。

installNewPackageLIF

我们以安装新 APK 为例,查看 installNewPackageLIF 的源码:

    /*
     * Install a non-existing package.
     */
    private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res, int installReason) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");

        ... ...

        try {
            // 扫描 APK
            PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                    System.currentTimeMillis(), user);

            // 更新 Settings 信息
            updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);

            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // 安装成功后,为新安装的应用程序准备数据
                prepareAppDataAfterInstallLIF(newPackage);

            } else {
                // 安装失败则删除 APK
                deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
            }
        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

installNewPackageLIF 主要做了以下 3 件事:

        ✨ 1、扫描 APK,将 APK 的信息存储在 PackageParser.Package 类型的 newPackage 中,一个 Package 的信息包含了 1 个 base APK 以及 0 个或者多个 split APK 。
        ✨ 2、更新该 APK 对应的 Settings 信息,Settings 用于保存所有包的动态设置。
        ✨ 3、如果安装成功就为新安装的应用程序准备数据,安装失败就删除APK。

scanPackageTracedLI

调用 scanPackageTracedLI() 进行安装 :

    public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
        try {
            return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

scanPackageLI - 01

scanPackageTracedLI() 调用了 scanPackageLI() 方法:

    /**
     *  Scans a package and returns the newly parsed package.
     *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
     */
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);
        pp.setCallback(mPackageParserCallback);
        ... ...

        return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
    }

scanPackageLI - 02

    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, long currentTime, @Nullable UserHandle user)
                    throws PackageManagerException {
        boolean success = false;
        try {
            // scanPackageDirtyLI 实际安装 package 的方法
            final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
                    currentTime, user);
            success = true;
            return res;
        } finally {
            if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
                // DELETE_DATA_ON_FAILURES is only used by frozen paths
                destroyAppDataLIF(pkg, UserHandle.USER_ALL,
                        StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
            }
        }
    }

总结

本文主要讲解了 PMS 是如何处理 APK 安装的流程,主要有几个步骤:

        ✨ PackageInstaller 安装 APK 时会将 APK 的信息交由 PMS 处理,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作。
        ✨ PMS 发送 INIT_COPY 和 MCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService ,完成复制 APK 等工作。
        ✨ 复制 APK 完成后,会开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作。

参考

 01. http://liuwangshu.cn/framewor...
 02. https://www.jianshu.com/p/c43...

12-01 00:28