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消息,则是主要检测安装文件的一致性,像包名、版本、签名。

相关推荐
_Shirley1 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei3 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng3 小时前
安卓多渠道apk配置不同签名
android
枫_feng3 小时前
AOSP开发环境配置
android·安卓
叶羽西4 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_171538855 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)7 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
mmsx7 小时前
android 登录界面编写
android·登录界面
姜毛毛-JYM7 小时前
【JetPack】Navigation知识点总结
android
花生糖@8 小时前
Android XR 应用程序开发 | 从 Unity 6 开发准备到应用程序构建的步骤
android·unity·xr·android xr