1 引言

在文章Android Xapk装置(一)中,讨论了xapk包的组成以及装置办法,并剖析了运用adb install-multiple办法装置xapk的进程,在该文章中,咱们知道,adb install-multiple指令装置xapk,实践上是在Android体系中运行execc:cmd package install-create,exec:cmd package install-write,exec:cmd package install-commit三个指令,而这三个指令终究会进入到PackageManagerServiceonShellCommand办法去履行。本文接着前文,剖析在PackageManagerService中的履行进程,本文的源码基于Android 10.

2 PackageManagerService中履行进程

2.1 onShellCommand

依据前文剖析,exec:cmd package指令终究会进入到PackageManagerService(源码位置:/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java)的onShellCommand办法中,其源码如下:

  @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, String[] args, ShellCallback callback,
            ResultReceiver resultReceiver) {
        (new PackageManagerShellCommand(this)).exec(
                this, in, out, err, args, callback, resultReceiver);
    }

onShellCommand办法中,会创立一个PackageManagerShellCommand方针,然后调用其exec办法.PackageManagerShellCommand源码坐落于/frameworks/base/services/core/java/com/android/server/pm/目录,PackageManagerShellCommand承继自ShellCommand(源码坐落/frameworks/base/core/java/android/os/ShellCommand.java),exec办法完结在ShellCommand类中,在该办法中,实践调用的是onCommand办法,而该办法是由各个子类去完结,所以实践上终究代码会进入大PackageManagerShellCommandonCommand办法中,该办法用来解析和PackageManagerService有关的相关指令,如pm dump,pm list这些指令终究也会进入到该办法。和本次装置xapk有关的处理代码如下

  public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }
        final PrintWriter pw = getOutPrintWriter();
        try {
            switch(cmd) {
                ...
                case "install-abandon":
                case "install-destroy":
                    return runInstallAbandon();
                case "install-commit":
                    return runInstallCommit();
                case "install-create":
                    return runInstallCreate();
                case "install-write":
                    return runInstallWrite();
                ...
            }
        } catch (RemoteException e) {
            pw.println("Remote exception: " + e);
        }
        return -1;
    }

从代码中能够看到,install-create,install-write,install-commit这三个指令别离由runInstallCreate,runInstallWrite,runInstallCommit三个办法履行,下面别离来看这三个办法

2.2 runInstallCreate

runInstallCreate办法源码如下

   private int runInstallCreate() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        final InstallParams installParams = makeInstallParams();
        final int sessionId = doCreateSession(installParams.sessionParams,
                installParams.installerPackageName, installParams.userId);
        // NOTE: adb depends on parsing this string
        pw.println("Success: created install session [" + sessionId + "]");
        return 0;
    }

在该办法中,先是结构了一个InstallParams方针,然后运用该方针做为参数,调用doCreateSession办法生成session并回来sessionId. InstallParamsPackageManagerShellCommand的一个内部类


    private static class InstallParams {
        SessionParams sessionParams;
        String installerPackageName;
        int userId = UserHandle.USER_ALL;
    }

从该类界说能够看到,InstallParams有三个成员变量:sessionParams:与session有关的参数,installerPackageName:调用装置程序或指令的运用,userId:本次装置的运用,对哪些用户可见,默许是一切用户.
了解了InstallParam的结构后,下面看结构InstallParam方针的makeInstallParams办法。在该办法中,首要创立了一个SessionParams方针,并将装置模式设为SessionParams.MODE_FULL_INSTALL,创立该方针后,创立一个InstallParams方针,并将SessionParams方针赋予其成员变量sessionParams.在完结这两步作业后,InstallParams方针就创立完结了。

 private InstallParams makeInstallParams() {
        final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
        final InstallParams params = new InstallParams();
        params.sessionParams = sessionParams;
        // Whitelist all permissions by default
        //PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS 是一个用于 Android 的 //PackageManager 类的常量。该常量指示装置包管理器在装置运用程序时,是否将该运用程序添加到一切白名单受限权限的白名单中,默许是添加
        sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;

办法剩下的部分便是为SessionParams方针赋值,首要为本次装置的运用赋予白名单权限,然后解析传递过来的参数,履行相应的战略,回忆前文, install-create指令:

exec:cmd package install-create -S size au.com.metrotrains.dwtd4.apk config.arm64_v8a.apk

在该指令中,有一个-S选项参数,在本办法中,和-S参数有关的代码如下

...
 switch (opt) {
                ....
                case "-S":
                    final long sizeBytes = Long.parseLong(getNextArg());
                    if (sizeBytes <= 0) {
                        throw new IllegalArgumentException("Size must be positive");
                    }
                    sessionParams.setSize(sizeBytes);
                    break;
                    ...

从代码中能够看到,关于选项-S,便是把本次装置的包总巨细写入到SessionParamsize特点中。至此,InstallParams方针就结构完结了,注意到,在该方针的三个特点中,installerPackageName特点为空。在完结InstallParams方针结构的剖析后,下面剖析doCreateSession办法,该办法是用来实践生成Session并回来sessionId:

    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
            throws RemoteException {
        userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_SYSTEM;
            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
        }
        final int sessionId = mInterface.getPackageInstaller()
                .createSession(params, installerPackageName, userId);
        return sessionId;
    }

在该办法中,首要对userId做了一些查看,首要也仍是权限方面,之后调用PackageInstallerServicecreateSession办法创立session,并回来sessionId

2.2.1 PackageInstallerService#createSession

PackageInstallerService 源码坐落frameworks/services/core/java/com/android/server/pm/PackageInstallerService,createSession源码如下:

 @Override
    public int createSession(SessionParams params, String installerPackageName, int userId) {
        try {
            return createSessionInternal(params, installerPackageName, userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }

createSession中,实践调用的是createSessionInternal办法完结详细的生成session作业,createSessionInsternal办法中和生成session有关的代码如下

        ...
        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            final int activeCount = getSessionCount(mSessions, callingUid);
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
            final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }
            sessionId = allocateSessionIdLocked();
        }
        final long createdMillis = System.currentTimeMillis();
        // We're staging to exactly one location
        File stageDir = null;
        String stageCid = null;
        if (!params.isMultiPackage) {
            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
                stageDir = buildSessionDir(sessionId, params);
            } else {
                stageCid = buildExternalStageCid(sessionId);
            }
        }
        session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
                mInstallThread.getLooper(), mStagingManager, sessionId, userId,
                installerPackageName, callingUid, params, createdMillis, stageDir, stageCid, false,
                false, false, null, SessionInfo.INVALID_ID, false, false, false,
                SessionInfo.STAGED_SESSION_NO_ERROR, "");
        synchronized (mSessions) {
            mSessions.put(sessionId, session);
        }
        if (params.isStaged) {
            mStagingManager.createSession(session);
        }
        if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) {
            mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        }
        writeSessionsAsync();
        return sessionId;
    }

在生成Session时,首要会查看当前体系中活跃的sessionId个数,有多少个sessionId就意味着现在体系有多个在装置的运用,所以需求操控sessionId个数,防止过多的运用一起装置拖垮体系;然后调用allocateSessionIdLocked办法生成一个随机的sessionId,在生成sessionId后,调用buildSessionDirbuindExternalStageCid办法别离生成stageDirstagCid,其间stageDir是用来临时寄存apk文件的目录,其目录相似/data/app/vmdl{sessionId}.tmp;在生成stageDirstageCid后,创立一个PackageInstallerSession方针作为本次装置的Session并将其和SessionId放在mapmSessions中,这样就将sessionIdSession关联起来了,PackageInstallerSession的源码坐落frameworks/services/core/java/com/android/server/pm/PackageInstallerSession,在结构办法中将这些参数保存到其成员变量中;在createSessionInternal办法的终究,调用writeSessionsAsync办法将session数据耐久化保存到目录/data/system/install_session.xml中,该文件保存着体系一切没有完结装置的session.若体系重启,PackageManagerService发动时会从该文件中读取session数据,持续完结装置

至此,install-create就完结了session的创立,并回来其对应的sessionId,下面看install-write的履行办法runInstallWrite

2.3 runInstallWrite

在履行完install-create指令创立好session后,接下来便是履行install-write办法写入apk文件,
回忆前文写入指令

exec:cmd package install-write -S apkSize sessionId au.com.metrotrains.dwtd4.apk -

install-write指令对应的履行办法便是runInstallWrite办法:

  private int runInstallWrite() throws RemoteException {
        long sizeBytes = -1;
        String opt;
        while ((opt = getNextOption()) != null) {
            if (opt.equals("-S")) {
                sizeBytes = Long.parseLong(getNextArg());
            } else {
                throw new IllegalArgumentException("Unknown option: " + opt);
            }
        }
        final int sessionId = Integer.parseInt(getNextArg());
        final String splitName = getNextArg();
        final String path = getNextArg();
        return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
    }

在该办法中,首要获取install-write指令带着的-S选项,该选项带着了本次写入apk文件的巨细,而且该参数是有必要的,获取到该参数后,再依次获取本次操作的sessionIdapk文件的途径,获取到这些参数后,调用doWriteSplit办法完结写入:

    private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
            boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            final PrintWriter pw = getOutPrintWriter();
            //1. 依据输入途径,翻开对应的文件,其间STDIN_PATH为'-''
            final ParcelFileDescriptor fd;
            if (STDIN_PATH.equals(inPath)) {
                fd = ParcelFileDescriptor.dup(getInFileDescriptor());
            } else if (inPath != null) {
                fd = openFileForSystem(inPath, "r");
                if (fd == null) {
                    return -1;
                }
                sizeBytes = fd.getStatSize();
                if (sizeBytes < 0) {
                    getErrPrintWriter().println("Unable to get size of: " + inPath);
                    return -1;
                }
            } else {
                fd = ParcelFileDescriptor.dup(getInFileDescriptor());
            }
            //2. 查看本次写入的文件的巨细
            if (sizeBytes <= 0) {
                getErrPrintWriter().println("Error: must specify a APK size");
                return 1;
            }
            //3. 依据seesionId翻开对应的PackageInstallerSession ,并调用其write办法
            session = new PackageInstaller.Session(
                    mInterface.getPackageInstaller().openSession(sessionId));
            session.write(splitName, 0, sizeBytes, fd);
            if (logSuccess) {
                pw.println("Success: streamed " + sizeBytes + " bytes");
            }
            return 0;
        } catch (IOException e) {
            getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
            return 1;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

能够看到,该办法首要分为3步:

  1. 依据指令传入的apk文件写入途径,翻开对应的文件,在上文中的指令中,传入的写入文件途径为-,其对应的便是STDIN_PATH,即写入规范写入中,
  2. 查看本次写入的apk文件巨细,写入的巨细应该大于0
  3. 依据传入的seesionId,翻开对应的Session,并调用其write办法完结写入Session在体系中对应的是PackageInstallerSession方针,所以实践上便是调用PackageInstallerSessionwrite办法。下面进入到PackageInstallerSession中,剖析其write办法

2.3.1 PackageInstallerSession#write

write办法源码如下

 @Override
    public void write(String name, long offsetBytes, long lengthBytes,
            ParcelFileDescriptor fd) {
        try {
            doWriteInternal(name, offsetBytes, lengthBytes, fd);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }

write办法中,实践上是调用doWriteInternal办法完结实践的写入操作:

 private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes,
            ParcelFileDescriptor incomingFd) throws IOException {
        // Quick sanity check of state, and allocate a pipe for ourselves. We
        // then do heavy disk allocation outside the lock, but this open pipe
        // will block any attempted install transitions.
        final RevocableFileDescriptor fd;
        final FileBridge bridge;
        final File stageDir;
        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();
            assertPreparedAndNotSealedLocked("openWrite");
            //1.获取写入装置包的stageDir,该值在创立session时创立,其途径相似/data/app/vmdl{sessionId}.tmp
            if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                fd = new RevocableFileDescriptor();
                bridge = null;
                mFds.add(fd);
            } else {
                fd = null;
                bridge = new FileBridge();
                mBridges.add(bridge);
            }
            stageDir = resolveStageDirLocked();
        }
        try {
            // Use installer provided name for now; we always rename later
            if (!FileUtils.isValidExtFilename(name)) {
                throw new IllegalArgumentException("Invalid name: " + name);
            }
            //2. 创立要写入的方针文件,一般便是stageDir/apkName.apk
            final File target;
            final long identity = Binder.clearCallingIdentity();
            try {
                target = new File(stageDir, name);
                 Slog.i("InstallTrace","PackageInstallerSeiion#dowriteInternal write apk in session: targetPath="+target.getAbsolutePath());
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
            // TODO: this should delegate to DCS so the system process avoids
            // holding open FDs into containers.
            final FileDescriptor targetFd = Os.open(target.getAbsolutePath(),
                    O_CREAT | O_WRONLY, 0644);
            Os.chmod(target.getAbsolutePath(), 0644);
            // If caller specified a total length, allocate it for them. Free up
            // cache space to grow, if needed.
            if (stageDir != null && lengthBytes > 0) {
                mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
                        PackageHelper.translateAllocateFlags(params.installFlags));
            }
            if (offsetBytes > 0) {
                Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
            }
            if (incomingFd != null) {
                switch (Binder.getCallingUid()) {
                    case android.os.Process.SHELL_UID:
                    case android.os.Process.ROOT_UID:
                    case android.os.Process.SYSTEM_UID:
                        break;
                    default:
                        throw new SecurityException(
                                "Reverse mode only supported from shell or system");
                }
                // In "reverse" mode, we're streaming data ourselves from the
                // incoming FD, which means we never have to hand out our
                // sensitive internal FD. We still rely on a "bridge" being
                // inserted above to hold the session active.
                try {
                    final Int64Ref last = new Int64Ref(0);
                    //3.将apk文件从规范输入(STDIN_FIENO)copy到方针文件中
                    FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null,
                            Runnable::run, (long progress) -> {
                                if (params.sizeBytes > 0) {
                                    final long delta = progress - last.value;
                                    last.value = progress;
                                    addClientProgress((float) delta / (float) params.sizeBytes);
                                }
                            });
                } finally {
                    IoUtils.closeQuietly(targetFd);
                    IoUtils.closeQuietly(incomingFd);
                    // We're done here, so remove the "bridge" that was holding
                    // the session active.
                    synchronized (mLock) {
                        if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                            mFds.remove(fd);
                        } else {
                            bridge.forceClose();
                            mBridges.remove(bridge);
                        }
                    }
                }
                return null;
            } else if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                fd.init(mContext, targetFd);
                return fd.getRevocableFileDescriptor();
            } else {
                bridge.setTargetFile(targetFd);
                bridge.start();
                return new ParcelFileDescriptor(bridge.getClientSocket());
            }
        } catch (ErrnoException e) {
            throw e.rethrowAsIOException();
        }
    }

能够看到,在该办法中,首要分为三步

  1. 获取写入装置包的临时目录stageDir该目录在install-create阶段生成,一般便是/data/app/vmdl{sessionId}.tmp目录
  2. 创立要写入的目录文件,一般便是stageDir/fileName.apk
  3. 调用FileUtils.copy办法将apk文件从规范输入中copy到方针文件中,完结本次的intall-write

在对一切的apk履行上述操作后,本次装置的xapk包就完结了install-write操作,下面剖析install-commit的履行办法runInstallCommit

2.4 runInstallCommit

在写入apk文件成功后,履行install-commit指令提交本次装置

 private int runInstallCommit() throws RemoteException {
        final int sessionId = Integer.parseInt(getNextArg());
        return doCommitSession(sessionId, true /*logSuccess*/);
    }

runInstallCommit办法中,实践是调用doCommitSession完结commit操作而在doCommitSession中,实践上是调用PacakgeInstallerSessioncommit办法并回来成果:


    private int doCommitSession(int sessionId, boolean logSuccess)
            throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        PackageInstaller.Session session = null;
        try {
            session = new PackageInstaller.Session(
                    mInterface.getPackageInstaller().openSession(sessionId));
            if (!session.isMultiPackage() && !session.isStaged()) {
                // Sanity check that all .dm files match an apk.
                // (The installer does not support standalone .dm files and will not process them.)
                try {
                    DexMetadataHelper.validateDexPaths(session.getNames());
                } catch (IllegalStateException | IOException e) {
                    pw.println(
                            "Warning [Could not validate the dex paths: " + e.getMessage() + "]");
                }
            }
            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            session.commit(receiver.getIntentSender());
            final Intent result = receiver.getResult();
            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                    PackageInstaller.STATUS_FAILURE);
            if (status == PackageInstaller.STATUS_SUCCESS) {
                if (logSuccess) {
                    pw.println("Success");
                }
            } else {
                pw.println("Failure ["
                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            }
            return status;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

下面进入到PacakgeInstallerSession中持续剖析commit办法

2.4.1 PackageInstallerSession#commit

commit源码如下


    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (mIsPerfLockAcquired && mPerfBoostInstall != null) {
            mPerfBoostInstall.perfLockRelease();
            mIsPerfLockAcquired = false;
        }
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + mParentSessionId +  " and may not be committed directly.");
        }
        //做一些准备作业,校验一下装置包,然后将运用标记为已提交
        if (!markAsCommitted(statusReceiver, forTransfer)) {
            return;
        }
        //判别是否是装置多个ap,adb install-multiple_packages指令,该值为true
        //adb install-mulitple指令装置的仍是一个apk,仅仅这个apk由多个子包构成,所以该值为false
        if (isMultiPackage()) {
            final SparseIntArray remainingSessions = mChildSessionIds.clone();
            final IntentSender childIntentSender =
                    new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
                            .getIntentSender();
            RuntimeException commitException = null;
            boolean commitFailed = false;
            for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
                final int childSessionId = mChildSessionIds.keyAt(i);
                try {
                    // commit all children, regardless if any of them fail; we'll throw/return
                    // as appropriate once all children have been processed
                    if (!mSessionProvider.getSession(childSessionId)
                            .markAsCommitted(childIntentSender, forTransfer)) {
                        commitFailed = true;
                    }
                } catch (RuntimeException e) {
                    commitException = e;
                }
            }
            if (commitException != null) {
                throw commitException;
            }
            if (commitFailed) {
                return;
            }
        }
        mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    }

在该办法中,首要会调用markAsCommitted办法对需求装置的运用做一些校验,首要是一些参数查看作业,并且在该阶段,会解分出装置的运用的包名并赋值给当前PackageInstallerSessionmPackageName特点,markAsComitted办法经过一系列调用后,终究会调用validateApkInstallLocked,该办法用来检测装置运用的各个参数,并完结一些装置的准备作业.该办法较长,下面只列出本次装置需求重视的当地:

private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
            throws PackageManagerException {
        ApkLite baseApk = null;
        mPackageName = null;
        mVersionCode = -1;
        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
        ...
        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
        if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
        }
        // Verify that all staged packages are internally consistent
        final ArraySet<String> stagedSplits = new ArraySet<>();
        for (File addedFile : addedFiles) {
            final ApkLite apk;
            try {
                 //1.解分出apk的ApkLite
                apk = PackageParser.parseApkLite(
                        addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
            } catch (PackageParserException e) {
                throw PackageManagerException.from(e);
            }
            if (!stagedSplits.add(apk.splitName)) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Split " + apk.splitName + " was defined multiple times");
            }
            // Use first package to define unknown values
            //2. 获取apk的packageName和versionCode,关于xapk,每一个子apk的包的packName,versionCode都是相同的
            if (mPackageName == null) {
                mPackageName = apk.packageName;
                mVersionCode = apk.getLongVersionCode();
            }
            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
                mSigningDetails = apk.signingDetails;
            }
            assertApkConsistentLocked(String.valueOf(addedFile), apk);
            // Take this opportunity to enforce uniform naming
            final String targetName;
            //3.主包改名为base.apk,非主包前面加上split_前缀
            if (apk.splitName == null) {
                targetName = "base" + APK_FILE_EXTENSION;
            } else {
                targetName = "split_" + apk.splitName + APK_FILE_EXTENSION;
            }
            if (!FileUtils.isValidExtFilename(targetName)) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Invalid filename: " + targetName);
            }
            final File targetFile = new File(mResolvedStageDir, targetName);
            resolveAndStageFile(addedFile, targetFile);
            // Base is coming from session
            //4.将主包(base包保存下来)
            if (apk.splitName == null) {
                mResolvedBaseFile = targetFile;
                baseApk = apk;
            }
            ....
         }
        }

在该办法中,会遍历stagedDir,即写入apk文件的目录,对每一个apk做如下操作:

  1. 解分出apk的ApkLite
  2. 获取apk的packageName和versionCode,关于xapk,每一个子apk的包的packName,versionCode都是相同的
  3. 主包改名为base.apk,非主包前面加上split_前缀
  4. 假如是主包,则将主包途径保存下来
    从代码中能够看到,要害的过程是第1步,即调用packageParser.parseApkLite办法生成ApkLite方针,ApkLite坐落源码/frameworks/base/core/java/android/content/pm/PackageParser.java首要是寄存AndroidMainfest.xml文件中跟装置运用有关的特点,如packageName,versionCode

    public static class ApkLite {
        public final String codePath;
        public final String packageName;
        public final String splitName;
        public boolean isFeatureSplit;
        public final String configForSplit;
        public final String usesSplitName;
        public final int versionCode;
        public final int versionCodeMajor;
        public final int revisionCode;
        public final int installLocation;
        public final int minSdkVersion;
        public final int targetSdkVersion;
        public final VerifierInfo[] verifiers;
        public final SigningDetails signingDetails;
        public final boolean coreApp;
        public final boolean debuggable;
        public final boolean multiArch;
        public final boolean use32bitAbi;
        public final boolean extractNativeLibs;
        public final boolean isolatedSplits;
        public final boolean isSplitRequired;
        public final boolean useEmbeddedDex;
   }

在完结运用的参数查看后,下一步判别是否是MutilPackage,即批量装置多个运用的场景,一般是false,所以在该办法中,实践上是向Handler中发送一个MSG_COMMITMessgae,在Handler中处理该消息的代码如下

 private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_COMMIT:
                    handleCommit();
                    break;
                //省掉其他case
            }
            return true;
        }
    };

在处理MSG_COMMIT时会调用handleCommit办法,该办法源码如下

  private void handleCommit() {
        //设备管理和设备安全相关,承认当前用户是否有权限装置运用
        if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
            DevicePolicyEventLogger
                    .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                    .setAdmin(mInstallerPackageName)
                    .write();
        }
        //该apk是否分段传输,假如分段传输,需求指定--staged选项,一般不必
        if (params.isStaged) {
            mStagingManager.commitSession(this);
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //是否装置apex
        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                    "APEX packages can only be installed using staged sessions.", null);
            return;
        }
        // For a multiPackage session, read the child sessions
        // outside of the lock, because reading the child
        // sessions with the lock held could lead to deadlock
        // (b/123391593).
        //多运用装置场景,获取一切的chidlSessions,单运用装置不必重视
        List<PackageInstallerSession> childSessions = getChildSessions();
        try {
            synchronized (mLock) {
                //提交一切的session
                commitNonStagedLocked(childSessions);
            }
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
            destroyInternal();
            dispatchSessionFinished(e.error, completeMsg, null);
        }
    }

在该办法中,做一些必要的校验后,假如是装置非stag应该和非apex,终究会进入到commitNonStagedLocked办法中办法中,关于单运用来说,childSessions参数为null,下面看commitNonStagedLocked办法

    @GuardedBy("mLock")
    private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
        //1.创立一个committingSession
        final PackageManagerService.ActiveInstallSession committingSession =
                makeSessionActiveLocked();
        if (committingSession == null) {
            return;
        }
        //处理多运用状况
        if (isMultiPackage()) {
             Slog.i("InstallTrace", " PackageInstallerSeiion#commitNonStagedLocked commit session: isMultiPackage");
            List<PackageManagerService.ActiveInstallSession> activeChildSessions =
                    new ArrayList<>(childSessions.size());
            boolean success = true;
            PackageManagerException failure = null;
            for (int i = 0; i < childSessions.size(); ++i) {
                final PackageInstallerSession session = childSessions.get(i);
                try {
                    final PackageManagerService.ActiveInstallSession activeSession =
                            session.makeSessionActiveLocked();
                    if (activeSession != null) {
                        activeChildSessions.add(activeSession);
                    }
                } catch (PackageManagerException e) {
                    failure = e;
                    success = false;
                }
            }
            if (!success) {
                try {
                    mRemoteObserver.onPackageInstalled(
                            null, failure.error, failure.getLocalizedMessage(), null);
                } catch (RemoteException ignored) {
                }
                return;
            }
            mPm.installStage(activeChildSessions);
        } else {
            //2.调用instllStage办法
            mPm.installStage(committingSession);
        }
    }

在该办法中,实践上就做了两件事:

  1. 调用makeSessionActiveLocked办法生成ActiveInstallSession方针
  2. 调用PackageManagerServiceinstallStage办法完结装置

下面先看makeSessionActiveLocked办法

    @GuardedBy("mLock")
    private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
            throws PackageManagerException {
        if (mRelinquished) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Session relinquished");
        }
        if (mDestroyed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
        }
        if (!mSealed) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
        }
        final IPackageInstallObserver2 localObserver;
        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
            localObserver = null;
        } else {
            Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: isMultipackage="+params.isMultiPackage);
            if (!params.isMultiPackage) {
                Preconditions.checkNotNull(mPackageName);
                Preconditions.checkNotNull(mSigningDetails);
                Preconditions.checkNotNull(mResolvedBaseFile);
                //1.是否还需求询问授权装置,关于adb装置一般不必
                if (needToAskForPermissionsLocked()) {
                    // User needs to confirm installation;
                    // give installer an intent they can use to involve
                    // user.
                    final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
                    intent.setPackage(mPm.getPackageInstallerPackageName());
                    intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
                    try {
                        mRemoteObserver.onUserActionRequired(intent);
                    } catch (RemoteException ignored) {
                    }
                    // Commit was keeping session marked as active until now; release
                    // that extra refcount so session appears idle.
                    closeInternal(false);
                    return null;
                }
                // Inherit any packages and native libraries from existing install that
                // haven't been overridden.
                //2.装置mode是否承继已有的package,adb install 一般是SessionParams.MODE_FULL_INSTALL
                if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
                    try {
                        final List<File> fromFiles = mResolvedInheritedFiles;
                        final File toDir = resolveStageDirLocked();
                        if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
                        if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
                            throw new IllegalStateException("mInheritedFilesBase == null");
                        }
                        if (isLinkPossible(fromFiles, toDir)) {
                            if (!mResolvedInstructionSets.isEmpty()) {
                                final File oatDir = new File(toDir, "oat");
                                createOatDirs(mResolvedInstructionSets, oatDir);
                            }
                            // pre-create lib dirs for linking if necessary
                            if (!mResolvedNativeLibPaths.isEmpty()) {
                                for (String libPath : mResolvedNativeLibPaths) {
                                    // "/lib/arm64" -> ["lib", "arm64"]
                                    final int splitIndex = libPath.lastIndexOf('/');
                                    if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
                                        Slog.e(TAG,
                                                "Skipping native library creation for linking due"
                                                        + " to invalid path: " + libPath);
                                        continue;
                                    }
                                    final String libDirPath = libPath.substring(1, splitIndex);
                                    final File libDir = new File(toDir, libDirPath);
                                    if (!libDir.exists()) {
                                        NativeLibraryHelper.createNativeLibrarySubdir(libDir);
                                    }
                                    final String archDirPath = libPath.substring(splitIndex + 1);
                                    NativeLibraryHelper.createNativeLibrarySubdir(
                                            new File(libDir, archDirPath));
                                }
                            }
                            linkFiles(fromFiles, toDir, mInheritedFilesBase);
                        } else {
                            // TODO: this should delegate to DCS so the system process
                            // avoids holding open FDs into containers.
                            copyFiles(fromFiles, toDir);
                        }
                    } catch (IOException e) {
                        throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                                "Failed to inherit existing install", e);
                    }
                }
                // TODO: surface more granular state from dexopt
                mInternalProgress = 0.5f;
                computeProgressLocked(true);
                // Unpack native libraries
                //3. 提取出lib库
                 Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: mResolvedStageDir="+mResolvedStageDir+"params.abi="+params.abiOverride);
                extractNativeLibraries(mResolvedStageDir, params.abiOverride,
                        mayInheritNativeLibs());
            }
            // We've reached point of no return; call into PMS to install the stage.
            // Regardless of success or failure we always destroy session.
            localObserver = new IPackageInstallObserver2.Stub() {
                @Override
                public void onUserActionRequired(Intent intent) {
                    throw new IllegalStateException();
                }
                @Override
                public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                        Bundle extras) {
                    destroyInternal();
                    dispatchSessionFinished(returnCode, msg, extras);
                }
            };
        }
        //4 确认use,一般是UserHandle.ALL
        final UserHandle user;
        if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }
        //5.回来AcitveInstallSession方针
        mRelinquished = true;
           Slog.i("InstallTrace", " PackageInstallerSeiion#makeSessionActiveLocaked commit session: mPackageName="+mPackageName+" stageDir="+stageDir+" mInstallerPackageName=" + mInstallerPackageName+" uid="+ mInstallerUid);
        return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
                localObserver, params, mInstallerPackageName, mInstallerUid, user,
                mSigningDetails);
    }

makeSessionActiveLocked办法中,首要有下面5个过程

  1. 是否还需求询问授权装置,关于adb装置一般不必
  2. 装置mode是否承继已有的package,adb install 一般是SessionParams.MODE_FULL_INSTALL
  3. 提取出lib
  4. 确认use,一般是UserHandle.ALL
  5. 回来AcitveInstallSession方针

这5个过程中,1,2是进行装置前的一些查看,4是承认运用的用户权限,5是回来生成的ActiveInstallSession方针,这四个过程都比较简单,比较重要的是第3步,提取运用的lib库,因为xapk的lib库和apk运用的lib库提取有一些差异,这部分内容放在下文独自剖析,咱们先持续看commitNonStagedLocked办法。

在调用makeSessionActiveLocked生成ActiveInstallSession后,commitNonStagedLocked办法下一步调用PackageManagerServiceinstallStage完结运用装置。在Android 10中,无论是在运用商铺下载运用自动装置,仍是下载apk手动装置,亦或是运用pm installadb install这些指令的办法装置,终究都会调用PackageManagerService#installStage办法完结运用装置,所以该办法不仅仅和xapk装置有联系。关于该办法的剖析,咱们放在下一篇中独自剖析。

在进入下一篇剖析installStage办法时,先看看xapk中比较重要的提取lib库的办法:PackageInstallSession中的extractNativeLibraries办法

3 Android运用提取lib库

3.1 Android 运用提取lib库全体流程

在android运用,尤其是游戏中,一般会包括许多的动态so库,若这些游戏是apk方式的装置包,其so库一般会打包到apk的lib目录下,在装置时,会将这些so库从apk中提取到游戏的lib目录下。而关于包括多个apk的xapk游戏装置包,其so库可能会和普通apk游戏装置包相同,将so库打包到xapk的主apk中,这样运用adb multiple-install装置时,会将so库解析到运用的lib目录;而更常见的状况是,运用会将so库打包成一个独自的apk,在运行游戏时,会将这个apk加载到内存中,以此来完结so库动态加载,关于这种状况,一般是不能将so库解压到运用的lib目录。这样,关于xapk的运用在装置时,就需求考虑是否将so库抽取到lib目录下,前文提到,在装置运用时,会调用PackageInstallSession中的extractNativeLibraries办法提取so库,下面看看该办法代码:

  private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
            throws PackageManagerException {
         //libDir:即lib目录
        final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
         packageDir.getAbsolutePath()+";abioverride="+abiOverride+";inherit="+inherit);
        //inherit一般为false
        if (!inherit) {
            // Start from a clean slate
            NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
        }
        NativeLibraryHelper.Handle handle = null;
        try {
            //依据packageDir目录生成对应的Handle
            handle = NativeLibraryHelper.Handle.create(packageDir);
            //解析so库到lib目录
            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
                    abiOverride);
            if (res != PackageManager.INSTALL_SUCCEEDED) {
                throw new PackageManagerException(res,
                        "Failed to extract native libraries, res=" + res);
            }
        } catch (IOException e) {
            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                    "Failed to extract native libraries", e);
        } finally {
            IoUtils.closeQuietly(handle);
        }
    }

从代码中能够看到,该办法首要在于NativeLibraryHelper.Handle.create NativeLibraryHelper.copyNativeBinariesWithOverride这两个办法的逻辑上,NativeLibraryHelper源码坐落frameworks/base/core/java/com/android/internal/content/NativeLibraryHelper.java,Handle是其一个内部类,该类的create办法为:

  public static class Handle implements Closeable {
        private final CloseGuard mGuard = CloseGuard.get();
        private volatile boolean mClosed;
        final long[] apkHandles;
        final boolean multiArch;
        final boolean extractNativeLibs;
        final boolean debuggable;
        public static Handle create(File packageFile) throws IOException {
            try {
                final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
                return create(lite);
            } catch (PackageParserException e) {
                throw new IOException("Failed to parse package: " + packageFile, e);
            }
        }
       ....

在该办法中,会先调用PackageParser.parsePackageLite办法,该办法和前文的packageParser.parseApkLite相似,会生成一个PackageLite方针,和ApkLite用来记载一个apk的要害信息不同,PackageLite用来记载整个装置包的要害信息,包括该装置包有多少个apk等信息:


    /**
     * Lightweight parsed details about a single package.
     */
    public static class PackageLite {
        @UnsupportedAppUsage
        public final String packageName;
        public final int versionCode;
        public final int versionCodeMajor;
        @UnsupportedAppUsage
        public final int installLocation;
        public final VerifierInfo[] verifiers;
        /** Names of any split APKs, ordered by parsed splitName */
        public final String[] splitNames;
        /** Names of any split APKs that are features. Ordered by splitName */
        public final boolean[] isFeatureSplits;
        /** Dependencies of any split APKs, ordered by parsed splitName */
        public final String[] usesSplitNames;
        public final String[] configForSplit;
        /**
         * Path where this package was found on disk. For monolithic packages
         * this is path to single base APK file; for cluster packages this is
         * path to the cluster directory.
         */
        public final String codePath;
        /** Path of base APK */
        public final String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
        public final String[] splitCodePaths;
        /** Revision code of base APK */
        public final int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        public final int[] splitRevisionCodes;
        public final boolean coreApp;
        public final boolean debuggable;
        public final boolean multiArch;
        public final boolean use32bitAbi;
        public final boolean extractNativeLibs;
        public final boolean isolatedSplits;

在生成PackageLite方针后,再调用其他create办法完结Handle创立,终究会将Handle的体系特点由PackageLite方针中获取到,比如其extractNativeLibs特点便是PackageLite方针的extractNativeLibs特点。其apkHandles便是PackageLite方针中保存的一切apk文件途径的文件描述符.
在生成了Handle方针后,下一步便是调用NativeLibraryHelper.copyNativeBinariesWithOverride办法完结so库的复制,在该办法中,终究会调用copyNativeBinaries办法完结so库复制

    /**
     * Copies native binaries to a shared library directory.
     *
     * @param handle APK file to scan for native libraries
     * @param sharedLibraryDir directory for libraries to be copied to
     * @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
     *         error code from that class if not
     */
    public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
        for (long apkHandle : handle.apkHandles) {
            int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
                    handle.extractNativeLibs, handle.debuggable);
            if (res != INSTALL_SUCCEEDED) {
                return res;
            }
        }
        return INSTALL_SUCCEEDED;
    }

在该办法中,会调用nativeCopyNativeBinaries方完结复制操作,该办法中的参数 handle.extractNativeLibs是用来标识是否需求提取so库,其值即为前文说的PackageLite方针的extractNativeLibs特点值。nativeCopyNativeBinaries是一个JNI办法,其完结坐落/frameworks/base/core/jni/com_android_internal_content_NativeLibraryHelper.cpp#com_android_internal_content_NativeLibraryHelper_copyNativeBinaries,在该办法中,终究会调用其该类的copyFileIfChanged办法,下面列出来该办法部分代码

copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
{
    void** args = reinterpret_cast<void**>(arg);
    jstring* javaNativeLibPath = (jstring*) args[0];
    jboolean extractNativeLibs = *(jboolean*) args[1];
    ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
    uint32_t uncompLen;
    uint32_t when;
    uint32_t crc;
    uint16_t method;
    off64_t offset;
    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
        ALOGE("Couldn't read zip entry info\n");
        return INSTALL_FAILED_INVALID_APK;
    }
    //假如extractNativeLibs特点为false,则仅仅做一些校验然后回来,不会copy so库
    if (!extractNativeLibs) {
        // check if library is uncompressed and page-aligned
        if (method != ZipFileRO::kCompressStored) {
            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
                fileName);
            return INSTALL_FAILED_INVALID_APK;
        }
        if (offset % PAGE_SIZE != 0) {
            ALOGE("Library '%s' is not page-aligned - will not be able to open it directly from"
                " apk.\n", fileName);
            return INSTALL_FAILED_INVALID_APK;
        }
        return INSTALL_SUCCEEDED;
    }
    //extractNativeLibs特点为true,解析一切apk的so库
    // Build local file path
    const size_t fileNameLen = strlen(fileName);
    char localFileName[nativeLibPath.size() + fileNameLen + 2];

在该办法中,会判别extractNativeLibs参数的值,该参数便是PackageLite方针中的extractNativeLibs特点值,若该值为true,则解析一切apk文件的so库,若该值为false,则不解析so库

至此,Android 运用提取so库的逻辑就剖析完结了,总结起来便是:依据生成的PackageLite方针的extractNativeLibs特点来判别是否需求将运用的so库解析到lib目录,若该值为true,则解析到lib目录,若该值为false,则不解析。下面咱们持续剖析PackageLite方针的创立进程。

3.2 PackageLite方针创立进程

前文提到,create办法中的PackageLite方针是经过PackageParser#parsePackageLite办法得到的,该办法源码如下

    public static PackageLite parsePackageLite(File packageFile, int flags)
            throws PackageParserException {
        if (packageFile.isDirectory()) {
            return parseClusterPackageLite(packageFile, flags);
        } else {
            return parseMonolithicPackageLite(packageFile, flags);
        }
    }

packageFile参数不是一个目录(即是一个apk文件),则履行parseMonolithicPackageLite办法:

    private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
            throws PackageParserException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
        final ApkLite baseApk = parseApkLite(packageFile, flags);
        final String packagePath = packageFile.getAbsolutePath();
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
    }

能够看到,在该办法中,便是调用parseApkLite解析apk文件,然后使用该apkLite生成一个PackageLite方针。
packageFile参数是一个目录,则履行办法parseClusterPackageLite办法,关于xapk来说,该参数一般便是一个目录,下面看该办法代码


    static PackageLite parseClusterPackageLite(File packageDir, int flags)
            throws PackageParserException {
        //1.获取该目录下一切apk文件
        final File[] files = packageDir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "No packages found in split");
        }
        String packageName = null;
        int versionCode = 0;
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
        for (File file : files) {
            //2.遍历apk文件
            if (isApkFile(file)) {
                //3.调用parseApkLite生成ApkLite
                final ApkLite lite = parseApkLite(file, flags);
                // Assert that all package names and version codes are
                // consistent with the first one we encounter.
                if (packageName == null) {
                    packageName = lite.packageName;
                    versionCode = lite.versionCode;
                } else {
                    //4.查看packageName和version,关于xapk包,每一个apk的packageName和versionCode应该共同
                    if (!packageName.equals(lite.packageName)) {
                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                "Inconsistent package " + lite.packageName + " in " + file
                                + "; expected " + packageName);
                    }
                    if (versionCode != lite.versionCode) {
                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                                "Inconsistent version " + lite.versionCode + " in " + file
                                + "; expected " + versionCode);
                    }
                }
                //5.将splitName和对应的ApkLite放入map表,注意,主包的splitName为null
                // Assert that each split is defined only once
                if (apks.put(lite.splitName, lite) != null) {
                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                            "Split name " + lite.splitName
                            + " defined more than once; most recent was " + file);
                }
            }
        }
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        //6. 移除去主包,并报错主包的ApkLite
        final ApkLite baseApk = apks.remove(null);
        if (baseApk == null) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Missing base APK in " + packageDir);
        }
        // Always apply deterministic ordering based on splitName
        final int size = apks.size();
        //7.获取splitNames等参数
        String[] splitNames = null;
        boolean[] isFeatureSplits = null;
        String[] usesSplitNames = null;
        String[] configForSplits = null;
        String[] splitCodePaths = null;
        int[] splitRevisionCodes = null;
        String[] splitClassLoaderNames = null;
        if (size > 0) {
            splitNames = new String[size];
            isFeatureSplits = new boolean[size];
            usesSplitNames = new String[size];
            configForSplits = new String[size];
            splitCodePaths = new String[size];
            splitRevisionCodes = new int[size];
            splitNames = apks.keySet().toArray(splitNames);
            Arrays.sort(splitNames, sSplitNameComparator);
            for (int i = 0; i < size; i++) {
                final ApkLite apk = apks.get(splitNames[i]);
                usesSplitNames[i] = apk.usesSplitName;
                isFeatureSplits[i] = apk.isFeatureSplit;
                configForSplits[i] = apk.configForSplit;
                splitCodePaths[i] = apk.codePath;
                splitRevisionCodes[i] = apk.revisionCode;
            }
        }
        //8.获取codePath,即packageDir的绝对途径(stagedDir的途径)
        final String codePath = packageDir.getAbsolutePath();
        //9.调用对应的结构办法生成PackageLite方针
        return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
                configForSplits, splitCodePaths, splitRevisionCodes);
    }

如代码注释所示,该办法首要有9个过程,在这9个过程中,也包括调用parseApkLite办法生成ApkLite方针的过程,下面先看看该办法:

    public static ApkLite parseApkLite(File apkFile, int flags)
            throws PackageParserException {
        return parseApkLiteInner(apkFile, null, null, flags);
    }
      private static ApkLite parseApkLiteInner(File apkFile, FileDescriptor fd, String debugPathName,
            int flags) throws PackageParserException {
        final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
        XmlResourceParser parser = null;
        ApkAssets apkAssets = null;
        try {
            try {
                apkAssets = fd != null
                        ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
                        : ApkAssets.loadFromPath(apkPath);
            } catch (IOException e) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                        "Failed to parse " + apkPath);
            }
            //读取apk的AndroidManifest.xml文件
            parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
            final SigningDetails signingDetails;
            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                // TODO: factor signature related items out of Package object
                final Package tempPkg = new Package((String) null);
                final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                try {
                    collectCertificates(tempPkg, apkFile, skipVerify);
                } finally {
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                signingDetails = tempPkg.mSigningDetails;
            } else {
                signingDetails = SigningDetails.UNKNOWN;
            }
            final AttributeSet attrs = parser;
            //解析AndroidManifest.xml文件中特点
            return parseApkLite(apkPath, parser, attrs, signingDetails);
        } catch (XmlPullParserException | IOException | RuntimeException e) {
            Slog.w(TAG, "Failed to parse " + apkPath, e);
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to parse " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
            if (apkAssets != null) {
                try {
                    apkAssets.close();
                } catch (Throwable ignored) {
                }
            }
            // TODO(b/72056911): Implement AutoCloseable on ApkAssets.
        }
    }

parseApkLite办法中,实践调用的是parseApkLiteInner,在该办法中,会读取该apk文件的AndroidManifest.xml文件,然后调用重载的parseApkLite办法生成ApkLite方针:


    private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
            SigningDetails signingDetails)
            throws IOException, XmlPullParserException, PackageParserException {
        final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
        int versionCode = 0;
        int versionCodeMajor = 0;
        int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
        int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
        int revisionCode = 0;
        boolean coreApp = false;
        boolean debuggable = false;
        boolean multiArch = false;
        boolean use32bitAbi = false;
        boolean extractNativeLibs = true;
        boolean isolatedSplits = false;
        boolean isFeatureSplit = false;
        boolean isSplitRequired = false;
        boolean useEmbeddedDex = false;
        String configForSplit = null;
        String usesSplitName = null;
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            final String attr = attrs.getAttributeName(i);
            if (attr.equals("installLocation")) {
                installLocation = attrs.getAttributeIntValue(i,
                        PARSE_DEFAULT_INSTALL_LOCATION);
            } else if (attr.equals("versionCode")) {
                versionCode = attrs.getAttributeIntValue(i, 0);
            } else if (attr.equals("versionCodeMajor")) {
                versionCodeMajor = attrs.getAttributeIntValue(i, 0);
            } else if (attr.equals("revisionCode")) {
                revisionCode = attrs.getAttributeIntValue(i, 0);
            } else if (attr.equals("coreApp")) {
                coreApp = attrs.getAttributeBooleanValue(i, false);
            } else if (attr.equals("isolatedSplits")) {
                isolatedSplits = attrs.getAttributeBooleanValue(i, false);
            } else if (attr.equals("configForSplit")) {
                configForSplit = attrs.getAttributeValue(i);
            } else if (attr.equals("isFeatureSplit")) {
                isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
            } else if (attr.equals("isSplitRequired")) {
                isSplitRequired = attrs.getAttributeBooleanValue(i, false);
            }
        }
        // Only search the tree when the tag is the direct child of <manifest> tag
        int type;
        final int searchDepth = parser.getDepth() + 1;
        final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }
            if (parser.getDepth() != searchDepth) {
                continue;
            }
            if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
                final VerifierInfo verifier = parseVerifier(attrs);
                if (verifier != null) {
                    verifiers.add(verifier);
                }
            } else if (TAG_APPLICATION.equals(parser.getName())) {
                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                    final String attr = attrs.getAttributeName(i);
                    if ("debuggable".equals(attr)) {
                        debuggable = attrs.getAttributeBooleanValue(i, false);
                    }
                    if ("multiArch".equals(attr)) {
                        multiArch = attrs.getAttributeBooleanValue(i, false);
                    }
                    if ("use32bitAbi".equals(attr)) {
                        use32bitAbi = attrs.getAttributeBooleanValue(i, false);
                    }
                    if ("extractNativeLibs".equals(attr)) {
                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                    }
                    if ("useEmbeddedDex".equals(attr)) {
                        useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
                    }
                }
            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
                if (usesSplitName != null) {
                    Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
                    continue;
                }
                usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name");
                if (usesSplitName == null) {
                    throw new PackageParserException(
                            PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                            "<uses-split> tag requires 'android:name' attribute");
                }
            } else if (TAG_USES_SDK.equals(parser.getName())) {
                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                    final String attr = attrs.getAttributeName(i);
                    if ("targetSdkVersion".equals(attr)) {
                        targetSdkVersion = attrs.getAttributeIntValue(i,
                                DEFAULT_TARGET_SDK_VERSION);
                    }
                    if ("minSdkVersion".equals(attr)) {
                        minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
                    }
                }
            }
        }
        return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
                minSdkVersion, targetSdkVersion);
    }

能够看到,在该办法中便是解析AndroidManifest.xml的首要特点,然后运用这个特点值生成ApkLite特点,其间需求注意这些特点的默许值,其间extractNativeLibs特点的默许值为true,表示需求将动态so库解析到lib目录,若该值为false,则表示不能把so库解析到lib目录。

在剖析了parseApkLite办法后,持续看parseClusterPackageLite,在该办法的终究,会调用PackageLite的结构办法生成一个PackageLite的方针


        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
                String[] splitCodePaths, int[] splitRevisionCodes) {
            this.packageName = baseApk.packageName;
            this.versionCode = baseApk.versionCode;
            this.versionCodeMajor = baseApk.versionCodeMajor;
            this.installLocation = baseApk.installLocation;
            this.verifiers = baseApk.verifiers;
            this.splitNames = splitNames;
            this.isFeatureSplits = isFeatureSplits;
            this.usesSplitNames = usesSplitNames;
            this.configForSplit = configForSplit;
            this.codePath = codePath;
            this.baseCodePath = baseApk.codePath;
            this.splitCodePaths = splitCodePaths;
            this.baseRevisionCode = baseApk.revisionCode;
            this.splitRevisionCodes = splitRevisionCodes;
            this.coreApp = baseApk.coreApp;
            this.debuggable = baseApk.debuggable;
            this.multiArch = baseApk.multiArch;
            this.use32bitAbi = baseApk.use32bitAbi;
            this.extractNativeLibs = baseApk.extractNativeLibs;
            this.isolatedSplits = baseApk.isolatedSplits;
        }

在该结构办法中,首要重视特点extractNativeLibs,该特点是主包(baseApk)的extractNativeLibs特点的值。该值默许是ture,若装置包的主包的AndroidManifest.xml文件中包括android:extractNativeLibs特点值,则运用其值,若不包括该特点,则运用默许值true。
至此,咱们就明白了PackageLite方针的extractNativeLibs特点是怎么来的了,其实便是解析主包的AndroidManifest.xml文件的android:extractNativeLibs特点值,若不包括该特点,则默许为true.在只包括一个apk的运用中(普通apk包或apk+obb方式的xapk包),其主包的AndroidManifest.xml文件中一般不会包括android:extractNativeLibs特点,而关于多个apk组成的xapk装置包,其主包的AndroidManifest.xml中一般都会清晰指定android:extractNativeLibs特点的值。

3.3 总结

经过对extractNativeLibraries办法的剖析,咱们得出了下面的结论:

  1. 若装置包主包的AndroidManifest.xml文件中不包括android:extractNativeLibs特点或该特点值为true,体系会解分出so库到lib目录
  2. 若装置包主包的AndroidManifest.xml文件中包括android:extractNativeLibs特点且该特点为false,体系不会解析so库到lib目录

剖析完了解析so库的逻辑,adb install-multiply的三个核心办法runInstallCreate, runInstallWrite, runInstallCommit三个办法全体流程就剖析完结了,在下一篇,咱们将持续剖析PackageManagerService#installStage办法,该办法完结终究的装置操作