Android 7.0 pm install apk 流程


Android 6.0时候,pm install apk 还是直接调用 PackageManager来执行:
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;
        }
}

Android 7.0的时候就改为了session 方式:

/*
     * Keep this around to support existing users of the "pm install" command that may not be
     * able to be updated [or, at least informed the API has changed] such as ddmlib.
     *
     * Moving the implementation of "pm install" to "cmd package install" changes the executing
     * context. Instead of being a stand alone process, "cmd package install" runs in the
     * system_server process. Due to SELinux rules, system_server cannot access many directories;
     * one of which being the package install staging directory [/data/local/tmp].
     *
     * The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
     */
    private int runInstall() throws RemoteException {
        final InstallParams params = makeInstallParams();
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);

        try {
            final String inPath = nextArg();
            if (inPath == null && params.sessionParams.sizeBytes == 0) {
                System.err.println("Error: must either specify a package size or an APK file");
                return 1;
            }
            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            System.out.println("Success");
            return 0;
        } finally {
            try {
                mInstaller.abandonSession(sessionId);
            } catch (Exception ignore) {
            }
        }
    }

Session 是 PackageInstaller 的新机制,主要调用下面接口:

public int createSession(android.content.pm.PackageInstaller.SessionParams params, java.lang.String installerPackageName, int userId) throws android.os.RemoteException;
public android.content.pm.IPackageInstallerSession openSession(int sessionId) throws android.os.RemoteException;

我们知道,Android的跨进程调用都是通过 binder 实现的,SystemServer中的 PackageInstallerService 负责创建session,然后这个session负责把用户提供的apk写到/data/local/tmp目录下,如何做的呢,就是通过 FileBridge,其实就是文件socket, SystemServer中创建一个文件描述符socket,用户进程通过session向这个socket发送apk数据,发送完了调用 session.commit 进行提交。PackagetInstallerSession.java 处理提交过程:

  private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
            throws PackageManagerException {
  ...........
  // Verify that stage looks sane with respect to existing application.
        // This currently only ensures packageName, versionCode, and certificate
        // consistency.
        validateInstallLocked(pkgInfo, appInfo);
  ..............
  mRelinquished = true;
        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                installerPackageName, installerUid, user, mCertificates);
   }

实际的安装过程都在 SystemServer 的 PMS 执行,PackageManagerService.java:

void installStage(String packageName, File stagedDir, String stagedCid,

IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,

String installerPackageName, int installerUid, UserHandle user,

Certificate[][] certificates) {

…………..

final Message msg = mHandler.obtainMessage(INIT_COPY);

…………..

mHandler.sendMessage(msg);

}

然后查看 PackageHandler 类的 doHandleMessage 函数,首先处理  INIT_COPY 消息,然后处理 MCS_BOUND 消息,

在 MCS_BOUND 消息中执行代码:

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”);
                            if (params.startCopy()) {

这个 startCopy 实现在 HandlerParams 类中:


private abstract class HandlerParams {
..........
final boolean startCopy() {
            boolean res;
            try {
                /// M: if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                if (++mRetries > MAX_RETRIES) {
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    handleStartCopy();
                    /// M: Slog.i(TAG, "Apk copy done");
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            handleReturnCode();
            return res;
        }

这个 handleStartCopy 函数也在 InstallParams 类中实现了,通过 FileInstallArgs 类的 copyApk 函数完成复制动作。

最后在 InstallParams 类中 handleReturnCode 函数中调用 processPendingInstall 函数, 这里发出了 POST_INSTALL 消息。

PMS 在 POST_INSTALL 消息处理中调用了 handlePackagePostInstall 函数,这个函数发出了 app 安装或者升级的广播:

// Send added for users that see the package for the first time
                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                        extras, 0 /*flags*/, null /*targetPackage*/,
                        null /*finishedReceiver*/, firstUsers);

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