Android 安装过程三 MSG_ON_SESSION_SEALED、MSG_STREAM_VALIDATE_AND_COMMIT的处理

Android 安装过程一 界面跳转 知道,在InstallInstalling Activity中,PackageInstallerSession对象创建之后,接着会打开它,然后将安装文件进行拷贝,拷贝完成之后,会对Session对象确认。

从Session对象确认往下看,Session对象在安装进程中对应是PackageInstaller.Session对象。它最终会进入系统进程调用到PackageInstallerSession对象的commit(@NonNull IntentSender statusReceiver, boolean forTransfer)方法,如下:

java 复制代码
    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + getParentSessionId() +  " and may not be committed directly.");
        }

        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        if (isMultiPackage()) {
            synchronized (mLock) {
                final IntentSender childIntentSender =
                        new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver)
                                .getIntentSender();
                boolean sealFailed = false;
                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                    // seal all children, regardless if any of them fail; we'll throw/return
                    // as appropriate once all children have been processed
                    if (!mChildSessions.valueAt(i)
                            .markAsSealed(childIntentSender, forTransfer)) {
                        sealFailed = true;
                    }
                }
                if (sealFailed) {
                    return;
                }
            }
        }

        dispatchSessionSealed();
    }

如果该Session对象有父Session,则会报异常。

接着调用markAsSealed(statusReceiver, forTransfer),在该方法里面它会将PackageInstallerSession的成员mRemoteStatusReceiver进行赋值为参数statusReceiver,并且会改变PackageInstallerSession的状态mSealed为true。mRemoteStatusReceiver是封装了广播的Intent对象,等待安装结果进行通知。

如果是多包的情况下,会设置子Session,并更改其mSealed。

这里尽量先说普通的单安装文件的情况,接着会调用dispatchSessionSealed()。看一下它的代码:

java 复制代码
    private void dispatchSessionSealed() {
        mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
    }

发送MSG_ON_SESSION_SEALED消息

它主要是发送MSG_ON_SESSION_SEALED消息。它会将消息发送到叫"PackageInstaller"的线程中进行处理。

"PackageInstaller"线程里会进行应用的安装工作,通过消息机制接收消息,这个从下面可以看出来。

"PackageInstaller"线程定义在PackageInstallerService类的构造函数中。

MSG_ON_SESSION_SEALED消息的处理在PackageInstallerSession的成员mHandlerCallback处(),如下:

java 复制代码
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_SESSION_SEALED:
                    handleSessionSealed();
                    break;
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
                case MSG_INSTALL:
                    handleInstall();
                    break;
                case MSG_ON_PACKAGE_INSTALLED:
                    final SomeArgs args = (SomeArgs) msg.obj;
                    final String packageName = (String) args.arg1;
                    final String message = (String) args.arg2;
                    final Bundle extras = (Bundle) args.arg3;
                    final IntentSender statusReceiver = (IntentSender) args.arg4;
                    final int returnCode = args.argi1;
                    args.recycle();

                    sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                            isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                            packageName, returnCode, message, extras);

                    break;
                case MSG_SESSION_VALIDATION_FAILURE:
                    final int error = msg.arg1;
                    final String detailMessage = (String) msg.obj;
                    onSessionValidationFailure(error, detailMessage);
                    break;
            }

            return true;
        }
    };

在这里会接着调用handleSessionSealed()进行处理。还可以看到,这里后面还会处理MSG_STREAM_VALIDATE_AND_COMMIT、MSG_INSTALL、MSG_ON_PACKAGE_INSTALLED、MSG_SESSION_VALIDATION_FAILURE消息。

handleSessionSealed()的代码如下:

java 复制代码
    private void handleSessionSealed() {
        assertSealed("dispatchSessionSealed");
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create.
        mCallback.onSessionSealedBlocking(this);
        dispatchStreamValidateAndCommit();
    }

    private void dispatchStreamValidateAndCommit() {
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }

可见它主要调用了mCallback的回调onSessionSealedBlocking方法,接着发送了MSG_STREAM_VALIDATE_AND_COMMIT消息。mCallback的定义是在PackageInstallerService类中,它是InternalCallback对象。看一下它的onSessionSealedBlocking方法实现:

java 复制代码
		public void onSessionSealedBlocking(PackageInstallerSession session) {
            // It's very important that we block until we've recorded the
            // session as being sealed, since we never want to allow mutation
            // after sealing.
            mSettingsWriteRequest.runNow();
        }

mSettingsWriteRequest它是一个RequestThrottle对象,他负责将PackageInstallerSession对象持久化。它主要涉及到"/data/system/install_sessions.xml"文件,它里面记录所有PackageInstallerSession对象的信息状态。"/data/system/install_sessions"目录下是对应应用的图标icon。

调用它的runNow()方法,就是将所有的PackageInstallerSession对象信息状态一起写入"/data/system/install_sessions.xml"文件。

发送MSG_STREAM_VALIDATE_AND_COMMIT消息

handleSessionSealed() 接着会发送MSG_STREAM_VALIDATE_AND_COMMIT消息,该消息的处理在PackageInstallerSession的成员mHandlerCallback处,参考上面。

它主要是执行handleStreamValidateAndCommit()函数:

java 复制代码
    private void handleStreamValidateAndCommit() {
        PackageManagerException unrecoverableFailure = null;
        // This will track whether the session and any children were validated and are ready to
        // progress to the next phase of install
        boolean allSessionsReady = false;
        try {
            allSessionsReady = streamValidateAndCommit();
        } catch (PackageManagerException e) {
            unrecoverableFailure = e;
        }

        ............

        if (!allSessionsReady) {
            return;
        }

        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }

这里省略了多包的安装,先看单APK的安装。

调用streamValidateAndCommit()得到结果allSessionsReady ,如果allSessionsReady为false,则发送MSG_INSTALL消息。

streamValidateAndCommit()对于单APK安装,主要是调用validateApkInstallLocked()函数,然后会将PackageInstallerSession对象的mCommitted = true。

接着看下validateApkInstallLocked()方法,该方法主要来检测相关内容的一致性。像包名、版本、签名。因为它存在split apk的安装方式,每个安装包都需要检查。它还会更改安装包的名字。

检测相关内容的一致性validateApkInstallLocked()

看一下validateApkInstallLocked()的代码,分段来看,分段一:

java 复制代码
    @GuardedBy("mLock")
    private PackageLite validateApkInstallLocked() throws PackageManagerException {
        ApkLite baseApk = null;
        PackageLite packageLite = null;
        mPackageLite = null;
        mPackageName = null;
        mVersionCode = -1;
        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;

        mResolvedBaseFile = null;
        mResolvedStagedFiles.clear();
        mResolvedInheritedFiles.clear();

        final PackageInfo pkgInfo = mPm.getPackageInfo(
                params.appPackageName, PackageManager.GET_SIGNATURES
                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);

        // Partial installs must be consistent with existing install
        if (params.mode == SessionParams.MODE_INHERIT_EXISTING
                && (pkgInfo == null || pkgInfo.applicationInfo == null)) {
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                    "Missing existing base package");
        }

        // Default to require only if existing base apk has fs-verity.
        mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled()
                && params.mode == SessionParams.MODE_INHERIT_EXISTING
                && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());

        final List<File> removedFiles = getRemovedFilesLocked();
        final List<String> removeSplitList = new ArrayList<>();
        if (!removedFiles.isEmpty()) {
            for (File removedFile : removedFiles) {
                final String fileName = removedFile.getName();
                final String splitName = fileName.substring(
                        0, fileName.length() - REMOVE_MARKER_EXTENSION.length());
                removeSplitList.add(splitName);
            }
        }

        final List<File> addedFiles = getAddedApksLocked();
        if (addedFiles.isEmpty() && removeSplitList.size() == 0) {
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                    String.format("Session: %d. No packages staged in %s", sessionId,
                          stageDir.getAbsolutePath()));
        }

        // Verify that all staged packages are internally consistent
        final ArraySet<String> stagedSplits = new ArraySet<>();
        final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
        for (File addedFile : addedFiles) {
            final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
                    addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
            if (result.isError()) {
                throw new PackageManagerException(result.getErrorCode(),
                        result.getErrorMessage(), result.getException());
            }

            final ApkLite apk = result.getResult();
            if (!stagedSplits.add(apk.getSplitName())) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Split " + apk.getSplitName() + " was defined multiple times");
            }

            // Use first package to define unknown values
            if (mPackageName == null) {
                mPackageName = apk.getPackageName();
                mVersionCode = apk.getLongVersionCode();
            }
            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
                mSigningDetails = apk.getSigningDetails();
            }

            assertApkConsistentLocked(String.valueOf(addedFile), apk);

            // Take this opportunity to enforce uniform naming
            final String targetName = ApkLiteParseUtils.splitNameToFileName(apk);
            if (!FileUtils.isValidExtFilename(targetName)) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Invalid filename: " + targetName);
            }

            // Yell loudly if installers drop attribute installLocation when apps explicitly set.
            if (apk.getInstallLocation() != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
                final String installerPackageName = getInstallerPackageName();
                if (installerPackageName != null
                        && (params.installLocation != apk.getInstallLocation())) {
                    Slog.wtf(TAG, installerPackageName
                            + " drops manifest attribute android:installLocation in " + targetName
                            + " for " + mPackageName);
                }
            }

            final File targetFile = new File(stageDir, targetName);
            resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());

            // Base is coming from session
            if (apk.getSplitName() == null) {
                mResolvedBaseFile = targetFile;
                baseApk = apk;
            } else {
                splitApks.put(apk.getSplitName(), apk);
            }
        }

        if (removeSplitList.size() > 0) {
            if (pkgInfo == null) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Missing existing base package for " + mPackageName);
            }

            // validate split names marked for removal
            for (String splitName : removeSplitList) {
                if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                            "Split not found: " + splitName);
                }
            }

            // ensure we've got appropriate package name, version code and signatures
            if (mPackageName == null) {
                mPackageName = pkgInfo.packageName;
                mVersionCode = pkgInfo.getLongVersionCode();
            }
            if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
                mSigningDetails = unsafeGetCertsWithoutVerification(
                        pkgInfo.applicationInfo.sourceDir);
            }
        }

首先根据包名得到包信息对象pkgInfo,如果是第一次安装则是null。

安装分两种模式,一个是全部安装(MODE_FULL_INSTALL),一个则是部分安装(MODE_INHERIT_EXISTING)。如果是部分安装模式,则之前已经安装过,不然会报PackageManagerException异常。

mVerityFoundForApks变量是控制fs-verity验证,它是确认安装包完整性的一个机制。系统支持、模式为部分安装、安装文件有fs-verity标识,这是这个变量为true的三个条件。

removedFiles是安装目录中去除文件。安装目录是在stageDir变量中,删除文件则是目录中以".removed"结尾的文件。接下来如果存在删除文件,则会将名字去掉".removed"存放在removeSplitList集合中。

addedFiles局部变量中存储的为stageDir目录中的apk文件。该目录中可能存在以".removed"、".dm"、".fsv_sig"、".digests"、".signature"结尾的文件,还包括apk文件。我们之前举例子中,它的名字为"PackageInstaller"。而addedFiles集合里装的就是安装apk文件,将其他的文件都会筛除掉。我们一般的apk安装都是一个文件,不过它还有split apks安装方式,会有多个apk文件,所以这里使用集合。

如果addedFiles集合和removeSplitList都为空,则会报PackageManagerException异常。

接着对addedFiles进行循环,下面看看每个循环里的工作。

首先解析安装包addedFile,得到ApkLite对象apk。

将apk的分包名(apk.getSplitName())添加进stagedSplits集合。基本包的getSplitName()为null。并且每个分包的分包名不允许相同,不然会报PackageManagerException异常。

mPackageName、mVersionCode、mSigningDetails都取第一次解析的安装包里的内容。接着会用assertApkConsistentLocked() 方法检查后面继续解析的安装包如果和之前的不一致,会报PackageManagerException异常。所以如果分包安装,每个分包的包名、版本、签名必须一样才行。

接着会生成新包名targetName。如果是基本包,为"base.apk";如果为分包,名字为"split_" + apk.getSplitName() + ".apk"。

如果安装包配置文件指定了安装位置和参数params的不同,会舍弃安装包配置文件里指定的。

然后生成目标文件targetFile。它的名字是上面说的targetName。

接着调用resolveAndStageFileLocked()方法,它主要是更改安装包及相关文件的名字。

例如目前的安装文件名为"PackageInstaller",之后会更改为"base.apk"。如果存在apk的fs-verity证书(".fsv_sig"结尾文件),也会进行名字修改,例如由"PackageInstaller.fsv_sig"改成"base.apk.fsv_sig"。同理,apk对应的".dm"、".digests"也会进行名字修改。

同时,这些修改之后的文件都会加入mResolvedStagedFiles集合中。

validateApkInstallLocked()继续会判断apk.getSplitName() == null,如此,它即为基本包。并将mResolvedBaseFile赋值为该基本包目标文件,baseApk = apk。如果不是基本包,会将分包加入splitApks中,它的key即为分包名。

接下来继续判断,removeSplitList中大小大于0的情况下。如果不存在已经安装的包,则报PackageManagerException异常。如果安装包信息对象pkgInfo.splitNames不包括删除的分包名,也会报PackageManagerException异常。继续检查mPackageName、mVersionCode和mSigningDetails的值,如果为空,则进行赋值。

接下来看validateApkInstallLocked()的代码,分段二:

java 复制代码
        if (isIncrementalInstallation() && !isIncrementalInstallationAllowed(mPackageName)) {
            throw new PackageManagerException(
                    PackageManager.INSTALL_FAILED_SESSION_INVALID,
                    "Incremental installation of this package is not allowed.");
        }

        if (mInstallerUid != mOriginalInstallerUid) {
            // Session has been transferred, check package name.
            if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals(
                    mOriginalInstallerPackageName)) {
                throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
                        "Can only transfer sessions that update the original installer");
            }
        }

        if (!mChecksums.isEmpty()) {
            throw new PackageManagerException(
                    PackageManager.INSTALL_FAILED_SESSION_INVALID,
                    "Invalid checksum name(s): " + String.join(",", mChecksums.keySet()));
        }

        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
            // Full installs must include a base package
            if (!stagedSplits.contains(null)) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Full install must include a base package");
            }
            if (baseApk.isSplitRequired() && stagedSplits.size() <= 1) {
                throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                        "Missing split for " + mPackageName);
            }
            // For mode full install, we compose package lite for future usage instead of
            // re-parsing it again and again.
            final ParseResult<PackageLite> pkgLiteResult =
                    ApkLiteParseUtils.composePackageLiteFromApks(input.reset(), stageDir, baseApk,
                            splitApks, true);
            if (pkgLiteResult.isError()) {
                throw new PackageManagerException(pkgLiteResult.getErrorCode(),
                        pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
            }
            mPackageLite = pkgLiteResult.getResult();
            packageLite = mPackageLite;
        } else {

如果安装Uid发生改变,包名是不允许改变的,不然会报PackageManagerException异常。

在完全安装的情况下,如果stagedSplits不包括null,则代表没有基础包,会报异常。如果基础包需要基础包,但是stagedSplits不包含基础包,也会报异常。接着会得到PackageLite对象,并将它赋值给mPackageLite和packageLite。

接下来看validateApkInstallLocked()的代码,分段三:

java 复制代码
        } else {
            final ApplicationInfo appInfo = pkgInfo.applicationInfo;
            ParseResult<PackageLite> pkgLiteResult = ApkLiteParseUtils.parsePackageLite(
                    input.reset(), new File(appInfo.getCodePath()), 0);
            if (pkgLiteResult.isError()) {
                throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                        pkgLiteResult.getErrorMessage(), pkgLiteResult.getException());
            }
            final PackageLite existing = pkgLiteResult.getResult();
            packageLite = existing;
            assertPackageConsistentLocked("Existing", existing.getPackageName(),
                    existing.getLongVersionCode());
            final PackageParser.SigningDetails signingDetails =
                    unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
            if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                        "Existing signatures are inconsistent");
            }

            // Inherit base if not overridden.
            if (mResolvedBaseFile == null) {
                mResolvedBaseFile = new File(appInfo.getBaseCodePath());
                inheritFileLocked(mResolvedBaseFile);
            }

            // Inherit splits if not overridden.
            if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
                for (int i = 0; i < existing.getSplitNames().length; i++) {
                    final String splitName = existing.getSplitNames()[i];
                    final File splitFile = new File(existing.getSplitApkPaths()[i]);
                    final boolean splitRemoved = removeSplitList.contains(splitName);
                    if (!stagedSplits.contains(splitName) && !splitRemoved) {
                        inheritFileLocked(splitFile);
                    }
                }
            }

            // Inherit compiled oat directory.
            final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
            mInheritedFilesBase = packageInstallDir;
            final File oatDir = new File(packageInstallDir, "oat");
            if (oatDir.exists()) {
                final File[] archSubdirs = oatDir.listFiles();

                // Keep track of all instruction sets we've seen compiled output for.
                // If we're linking (and not copying) inherited files, we can recreate the
                // instruction set hierarchy and link compiled output.
                if (archSubdirs != null && archSubdirs.length > 0) {
                    final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
                    for (File archSubDir : archSubdirs) {
                        // Skip any directory that isn't an ISA subdir.
                        if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
                            continue;
                        }

                        File[] files = archSubDir.listFiles();
                        if (files == null || files.length == 0) {
                            continue;
                        }

                        mResolvedInstructionSets.add(archSubDir.getName());
                        mResolvedInheritedFiles.addAll(Arrays.asList(files));
                    }
                }
            }

            // Inherit native libraries for DONT_KILL sessions.
            if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
                File[] libDirs = new File[]{
                        new File(packageInstallDir, NativeLibraryHelper.LIB_DIR_NAME),
                        new File(packageInstallDir, NativeLibraryHelper.LIB64_DIR_NAME)};
                for (File libDir : libDirs) {
                    if (!libDir.exists() || !libDir.isDirectory()) {
                        continue;
                    }
                    final List<String> libDirsToInherit = new ArrayList<>();
                    final List<File> libFilesToInherit = new ArrayList<>();
                    for (File archSubDir : libDir.listFiles()) {
                        if (!archSubDir.isDirectory()) {
                            continue;
                        }
                        String relLibPath;
                        try {
                            relLibPath = getRelativePath(archSubDir, packageInstallDir);
                        } catch (IOException e) {
                            Slog.e(TAG, "Skipping linking of native library directory!", e);
                            // shouldn't be possible, but let's avoid inheriting these to be safe
                            libDirsToInherit.clear();
                            libFilesToInherit.clear();
                            break;
                        }

                        File[] files = archSubDir.listFiles();
                        if (files == null || files.length == 0) {
                            continue;
                        }

                        libDirsToInherit.add(relLibPath);
                        libFilesToInherit.addAll(Arrays.asList(files));
                    }
                    for (String subDir : libDirsToInherit) {
                        if (!mResolvedNativeLibPaths.contains(subDir)) {
                            mResolvedNativeLibPaths.add(subDir);
                        }
                    }
                    mResolvedInheritedFiles.addAll(libFilesToInherit);
                }
            }
            // For the case of split required, failed if no splits existed
            if (packageLite.isSplitRequired()) {
                final int existingSplits = ArrayUtils.size(existing.getSplitNames());
                final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
                final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
                        && stagedSplits.contains(null));
                if (allSplitsRemoved && (stagedSplits.isEmpty() || onlyBaseFileStaged)) {
                    throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                            "Missing split for " + mPackageName);
                }
            }
        }

这是安装模式为部分安装的情况。由于它已经存在安装包,所以它需要继承存在安装包的一些信息。包括基础包、分包、编译的oat目录、本地库。这些代码主要就是进行的这些处理。

首先它会解析存在安装包的信息生成PackageLite对象existing,并将它赋值给变量packageLite。接着它会调用assertPackageConsistentLocked(),在这里它是检查,待安装文件和已安装文件的包名、版本是否一致。

接下来它会继续检查待安装文件和已安装文件的签名是否一致。

如果待安装文件的基本包没有设置,将它设置为当前已安装文件的基本包文件。同时会调用inheritFileLocked(mResolvedBaseFile),它有点类似于resolveAndStageFileLocked(),不过将文件添加到的集合使用的是成员变量mResolvedInheritedFiles,添加到它里面代表需要解析继承的文件。它也会对对应文件相关的".dm"、".fsv_sig"、".digests"、".signature"结尾的文件进行处理。

继续对分包处理,分包文件都在existing.getSplitApkPaths()中。所以在不删除的情况下,也调用inheritFileLocked(splitFile)进行处理。

接着是对已安装目录下面的"oat"目录进行处理,它里面是对应的处理的指令。处理完之后,将指令架构的名字添加到mResolvedInstructionSets中,将指令文件添加到mResolvedInheritedFiles中。

再下面是对本地库文件的处理。它们是在安装文件目录下的"lib"和"lib64"文件中。双循环中的变量为对应ABI路径,例如"armeabi"。最后会将下面的库文件加入mResolvedInheritedFiles集合中。

如果安装包需要分包,但是分包不存在或者需要删除,会报PackageManagerException异常。

validateApkInstallLocked()的最后代码,分段四:

java 复制代码
        if (packageLite.isUseEmbeddedDex()) {
            for (File file : mResolvedStagedFiles) {
                if (file.getName().endsWith(".apk")
                        && !DexManager.auditUncompressedDexInApk(file.getPath())) {
                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                            "Some dex are not uncompressed and aligned correctly for "
                            + mPackageName);
                }
            }
        }

        final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
        if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
            if (!packageLite.isDebuggable() && !packageLite.isProfileableByShell()) {
                mIncrementalFileStorages.disallowReadLogs();
            }
        }
        return packageLite;
    }

如果安装包设置了mUseEmbeddedDex,还会检查apk文件中".dex"文件是否是压缩状态,是否是对齐了。如果是压缩或者没对齐,都会报异常。

最后将安装包packageLite返回。

这样会回到handleStreamValidateAndCommit()方法中,如果没有什么异常,他会继续发送MSG_INSTALL消息。

总结一下,MSG_ON_SESSION_SEALED消息,会将Session对象的状态持久化到文件中,代表封装完毕。

MSG_STREAM_VALIDATE_AND_COMMIT消息,则是主要检测安装文件的一致性,像包名、版本、签名。

相关推荐
大白要努力!1 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟2 小时前
Android音频采集
android·音视频
小白也想学C3 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程3 小时前
初级数据结构——树
android·java·数据结构
闲暇部落6 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX8 小时前
Android 分区相关介绍
android
大白要努力!8 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee9 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood9 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-12 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记