APK安装流程详解15——PMS中的新安装流程下(装载)补充

APK安装流程系列文章整体内容如下:

本片文章的主要内容如下:

  • 1、PackageParser#setSeparateProcesses(String[] procs)方法解析
  • 2、PackageManagerService#shouldCheckUpgradeKeySetLP(PackageSetting, int) 方法解析
  • 3、PackageManagerService#checkUpgradeKeySetLP(PackageSetting, PackageParser.Package) 方法解析
  • 4、PackageManagerService#verifySignaturesLP(PackageSetting, PackageParser.Package)方法解析
  • 5、PackageDexOptimizer#performDexOp(PackageParser.Package, String[], String[], boolean, String,CompilerStats.PackageStats)方法解析
  • 6、args.doRename(res.returnCode, pkg, oldCodePath)方法解析
  • 7、startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg)方法解析

一、 PackageParser#setSeparateProcesses(String[] procs)方法解析

代码位置在PackageManagerService的installPackageLI方法里面会调用到,代码如下:
PackageManagerService.java

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setDisplayMetrics(mMetrics);
        ...
    }

可以看到,这里构造了一个PackageParser对象,然后设置了mSeparateProcesses属性。

mSeparateProcesses是一个数组,表示独立的进程名列表,这个参数是在PackageManagerService的构造函数中调用到,以后会分析一下函数是在什么地方调用,所以看mSeparateProcesses的获取过程:

代码在PackageManagerService.java 1838行

        String separateProcesses = SystemProperties.get("debug.separate_processes");
        if (separateProcesses != null && separateProcesses.length() > 0) {
            if ("*".equals(separateProcesses)) {
                mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
                mSeparateProcesses = null;
                Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
            } else {
                mDefParseFlags = 0;
                mSeparateProcesses = separateProcesses.split(",");
                Slog.w(TAG, "Running with debug.separate_processes: "
                        + separateProcesses);
            }
        } else {
            mDefParseFlags = 0;
            mSeparateProcesses = null;
        }    

从系统属性中读取debug.separate_processes属性,如果改属性返回值不为空,表示设置了该属性,否则系统未设置改属性,如果值等于则mSeparateProcesses为空,如果不为,则逗号分隔该字符串,解析每个独立的进程名,那个debug.separate_processes究竟有什么用?

那我们就来看下debug.separate_processes的作用:

separate_processes可以让应用程序的组件运行在自己的进程里面,separate_processes一般有两种设置:

  • 如果设置了"setprop debug.separate_processes",则将设置这个每个包中的每个进程。
  • 如果设置"setprop debug.separate_processes 'com.google.process.content,com.google.android.samples' "它只会影响项目清单中的指定进程("com.google.process.content,com.google.android.samples")。或者在AndroidManifest里面显式的设置"android:process"标记。
PS:虽然这样可以将一个进程拆分出来,或者多个进程组合成一个进程(他们必须来自同一个包)。它会强制所有受影响的组件在自己的.apk运行。

二、 PackageManagerService#shouldCheckUpgradeKeySetLP(PackageSetting, int) 方法解析

这个方法在PackageManagerService的installPackageLI方法里面被调用。代码在代码在PackageManagerService.java 12346行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
             ...
        }
        ...
   }

那我们来看下shouldCheckUpgradeKeySetLP这个方法的内部实现
代码在PackageManagerService.java 11807行

    private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
        // Can't rotate keys during boot or if sharedUser.
         // 判断是否可以进行升级验证的条件
        if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null
                || !oldPs.keySetData.isUsingUpgradeKeySets()) {
            return false;
        }
        // app is using upgradeKeySets; make sure all are valid
        KeySetManagerService ksms = mSettings.mKeySetManagerService;
        // 获取老的keySet数组
        long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();
        for (int i = 0; i < upgradeKeySets.length; i++) {
            // 遍历keySet数组,检查是否有对应的密钥集
            if (!ksms.isIdValidKeySetId(upgradeKeySets[i])) {
                // 如果对应的密钥集合,说明签名密钥有问题,则返回false
                Slog.wtf(TAG, "Package "
                         + (oldPs.name != null ? oldPs.name : "<null>")
                         + " contains upgrade-key-set reference to unknown key-set: "
                         + upgradeKeySets[i]
                         + " reverting to signatures check.");
                return false;
            }
        }
     // 如果所有的密钥都能对上,说明密钥没有问题,则返回true
        return true;
    }

通过注释我们知道,方法主要检查密钥集合是否和老版本的一致,如果不一致,则返回false。如果一致则返回true。

三、 PackageManagerService#checkUpgradeKeySetLP(PackageSetting, PackageParser.Package) 方法解析

这个方法在PackageManagerService的installPackageLI方法里面被调用。代码在代码在PackageManagerService.java 12347行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        if (!checkUpgradeKeySetLP(ps, pkg)) {
             ...
        }
        ...
   }

那我们来看下checkUpgradeKeySetLP这个方法的内部实现
代码在PackageManagerService.java 11829行

    private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) {
        // Upgrade keysets are being used.  Determine if new package has a superset of the
        // required keys.
        // 如果升级KeySet,确保新的安装包是否有超集的keys
        // 获取旧版本的KeySet数组
        long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
        KeySetManagerService ksms = mSettings.mKeySetManagerService;
        // 遍历KeySet数组
        for (int i = 0; i < upgradeKeySets.length; i++) {
             // 根据密钥获取公钥
            Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
            if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {
                 // 如果对应上 则返回true,
                return true;
            }
        }
        // 遍历都没有符合的,则返回false
        return false;
    }

这个方法内部主要检查是否有匹配的公钥,如果有则返回true,没有则返回false。

四、 PackageManagerService#verifySignaturesLP(PackageSetting, PackageParser.Package)方法解析

这个方法在PackageManagerService的installPackageLI方法里面被调用。代码在PackageManagerService.java 12355行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
         verifySignaturesLP(pkgSetting, pkg);
        ...
   }

那我们来看下verifySignaturesLP这个方法的内部实现
代码在PackageManagerService.java 11829行

    private void verifySignaturesLP(PackageSetting pkgSetting, PackageParser.Package pkg)
            throws PackageManagerException {
         // 第一步
        if (pkgSetting.signatures.mSignatures != null) {
            // Already existing package. Make sure signatures match
            //如果有旧版本,则查看旧版本的签名是否匹配
            boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
                    == PackageManager.SIGNATURE_MATCH;
            if (!match) {
                match = compareSignaturesCompat(pkgSetting.signatures, pkg)
                        == PackageManager.SIGNATURE_MATCH;
            }
            if (!match) {
                match = compareSignaturesRecover(pkgSetting.signatures, pkg)
                        == PackageManager.SIGNATURE_MATCH;
            }
            if (!match) {
                throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                        + pkg.packageName + " signatures do not match the "
                        + "previously installed version; ignoring!");
            }
        }

        // Check for shared user signatures
        // 第二步
        if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
           // 如果有共享用户,则检验共享用户的签名
            // Already existing package. Make sure signatures match
            boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
                    pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
            if (!match) {
                match = compareSignaturesCompat(pkgSetting.sharedUser.signatures, pkg)
                        == PackageManager.SIGNATURE_MATCH;
            }
            if (!match) {
                match = compareSignaturesRecover(pkgSetting.sharedUser.signatures, pkg)
                        == PackageManager.SIGNATURE_MATCH;
            }
            if (!match) {
                throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
                        "Package " + pkg.packageName
                        + " has no signatures that match those in shared user "
                        + pkgSetting.sharedUser.name + "; ignoring!");
            }
        }

这个方法其实很简单,分为两个部分

  • 如果有老版本的签名 则检查老版本的签名和新安装包的签名是否一致
  • 如果有共享用户的签名,则检查共享用户的签名与新安装包的签名是否一致。

里面验证签名都是三重机制,如下:

  • 第一重校验:调用compareSignatures方法,比较就的APK的签名和新的APK签名是否相同,如果返回值是PackageManager.SIGNATURE_MATCH,则通过并且不用后续校验,没有通过则进行第二重校验。
  • 第二重校验:调用compareSignaturesCompat方法,比较就的APK的签名和新的APK签名是否相同,如果返回值是PackageManager.SIGNATURE_MATCH,则通过并且不进行后续校验,没有通过则进行第二重校验。
  • 第三重校验:调用compareSignaturesRecover方法,比较旧的APK的签名和新的APK签名是否相同,如果返回值是PackageManager.SIGNATURE_MATCH,则通过并且不进行后续校验,没有通过则抛出异常,结束执行。

那我们就依次看下这三个方法

(一)、compareSignatures(Signature[] s1, Signature[] s2)方法解析

代码在PackageManagerService.java 3951行

    /**
     * Compares two sets of signatures. Returns:
     * <br />
     * {@link PackageManager#SIGNATURE_NEITHER_SIGNED}: if both signature sets are null,
     * <br />
     * {@link PackageManager#SIGNATURE_FIRST_NOT_SIGNED}: if the first signature set is null,
     * <br />
     * {@link PackageManager#SIGNATURE_SECOND_NOT_SIGNED}: if the second signature set is null,
     * <br />
     * {@link PackageManager#SIGNATURE_MATCH}: if the two signature sets are identical,
     * <br />
     * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ.
     */
    static int compareSignatures(Signature[] s1, Signature[] s2) {
        if (s1 == null) {
            return s2 == null
                    ? PackageManager.SIGNATURE_NEITHER_SIGNED
                    : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
        }

        if (s2 == null) {
            return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
        }

        if (s1.length != s2.length) {
            return PackageManager.SIGNATURE_NO_MATCH;
        }

        // Since both signature sets are of size 1, we can compare without HashSets.
        if (s1.length == 1) {
            return s1[0].equals(s2[0]) ?
                    PackageManager.SIGNATURE_MATCH :
                    PackageManager.SIGNATURE_NO_MATCH;
        }

        ArraySet<Signature> set1 = new ArraySet<Signature>();
        for (Signature sig : s1) {
            set1.add(sig);
        }
        ArraySet<Signature> set2 = new ArraySet<Signature>();
        for (Signature sig : s2) {
            set2.add(sig);
        }
        // Make sure s2 contains all signatures in s1.
        if (set1.equals(set2)) {
            return PackageManager.SIGNATURE_MATCH;
        }
        return PackageManager.SIGNATURE_NO_MATCH;
    }

上代码很简单就是先做非空判断,然后把两个数组转化成ArraySet,然后判断两个ArraySet是否相同,如果相同则返回PackageManager.SIGNATURE_MATCH,如果不相同则返回PackageManager.SIGNATURE_NO_MATCH

(二)、compareSignaturesCompat(PackageSignatures,PackageParser.Package)方法解析

如果上面的匹配不符合则说明当前不匹配,所我们要考虑是不是版本的的问题,所以就有了这个方法。

代码在PackageManagerService.java 4004行

    /**
     * Used for backward compatibility to make sure any packages with
     * certificate chains get upgraded to the new style. {@code existingSigs}
     * will be in the old format (since they were stored on disk from before the
     * system upgrade) and {@code scannedSigs} will be in the newer format.
     */
    private int compareSignaturesCompat(PackageSignatures existingSigs,
            PackageParser.Package scannedPkg) {
        // 第一步
        // 更新安装包名的签名版本是否小于数据库中签名版本
        if (!isCompatSignatureUpdateNeeded(scannedPkg)) {
            // 如果大于,则直接返回不匹配
            return PackageManager.SIGNATURE_NO_MATCH;
        }
       // 第二步
        ArraySet<Signature> existingSet = new ArraySet<Signature>();
        for (Signature sig : existingSigs.mSignatures) {
            existingSet.add(sig);
        }
        ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
        for (Signature sig : scannedPkg.mSignatures) {
            try {
                Signature[] chainSignatures = sig.getChainSignatures();
                for (Signature chainSig : chainSignatures) {
                    scannedCompatSet.add(chainSig);
                }
            } catch (CertificateEncodingException e) {
                scannedCompatSet.add(sig);
            }
        }
        // 第三步
        /*
         * Make sure the expanded scanned set contains all signatures in the
         * existing one.
         */
        if (scannedCompatSet.equals(existingSet)) {
            // Migrate the old signatures to the new scheme.
            // 签名替换
            existingSigs.assignSignatures(scannedPkg.mSignatures);
            // The new KeySets will be re-added later in the scanning process.
            synchronized (mPackages) {
                mSettings.mKeySetManagerService.removeAppKeySetDataLPw(scannedPkg.packageName);
            }
            return PackageManager.SIGNATURE_MATCH;
        }
        // 如果最后不匹配则返回 不匹配
        return PackageManager.SIGNATURE_NO_MATCH;
    }

先来看下注释:

这个方法主要是保证向后的兼容性,这样可以确保证书链上的包可以升级到最新的版本。existingSigs是旧格式,因为它在升级前是在磁盘空间上,scansSigs是新的格式。

我将这个方法分为三个步骤

  • 第一步:判断升级包的签名版本是否小于当前系统中签名的数据库版本号,上面一层判断已经不匹配才会走到这个方法里面,所以如果更新的安装包的签名版本大于当前数据库中的签名版本号,则一定是不匹配的。所以会返回PackageManager.SIGNATURE_NO_MATCH
  • 第二步:同样是通过遍历的方式把旧的签名数组转化为ArraySet对象existingSet,同时遍历新的安装包中每个签名的签名链,并把签名链加入到ArraySet对象
  • 第三步:确保扩展的扫描集包含现有的所有签名。如果scannedCompatSet和existingSet一致,则进行签名替换,并且在mSettings.mKeySetManagerService删除签名。

PS:上文说的DatabaseVersion 其实是Settings.java的内部类

(三)、compareSignaturesRecover(PackageSignatures,PackageParser.Package)方法解析

如果上面两个匹配规则都没有匹配,我们考虑是不是在证书有过变动导致的匹配失败,所以这个方法主要考虑是否恢复证书进行匹配
代码在PackageManagerService.java 4046行

    private int compareSignaturesRecover(PackageSignatures existingSigs,
            PackageParser.Package scannedPkg) {
        // 第一步
        if (!isRecoverSignatureUpdateNeeded(scannedPkg)) {
            return PackageManager.SIGNATURE_NO_MATCH;
        }
        // 第二步
        String msg = null;
        try {
            if (Signature.areEffectiveMatch(existingSigs.mSignatures, scannedPkg.mSignatures)) {
                logCriticalInfo(Log.INFO, "Recovered effectively matching certificates for "
                        + scannedPkg.packageName);
                return PackageManager.SIGNATURE_MATCH;
            }
        } catch (CertificateException e) {
            msg = e.getMessage();
        }

        logCriticalInfo(Log.INFO,
                "Failed to recover certificates for " + scannedPkg.packageName + ": " + msg);
        return PackageManager.SIGNATURE_NO_MATCH;
    }

我将上面的方法分为两步:

  • 第一步:调用isCompatSignatureUpdateNeeded判断是否有恢复的需求,这里随带说下isCompatSignatureUpdateNeeded方法。Android LOLLIPOP这个版本是一个时间窗口,会对证书进行修该。如果不用进行证书恢复,则整个这个方法就无意义了,直接返回PackageManager.SIGNATURE_NO_MATCH
  • 第二步:调用Signature的静态方法areEffectiveMatch进行匹配,这个方法内部。由于在极少数情况下,证书可能会有错误的编码,导致匹配失败。这个方法就是避免这种情况的解决方案,代码不多,大家有兴趣可以自行查看。

五、PackageDexOptimizer#performDexOp(PackageParser.Package, String[], String[], boolean, String,CompilerStats.PackageStats)方法解析

代码位置在PackageManagerService的installPackageLI方法里面会调用到,代码如下:
PackageManagerService.java 12451行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
           // Run dexopt before old package gets removed, to minimize time when app is unavailable
            int result = mPackageDexOptimizer
                    .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,false /* defer */, false /* inclDependencies */, true /* boot complete */);
        ...
    }

那我们就来看下PackageDexOptimizer#performDexOp方法
代码在PackageDexOptimizer.java 73行

    /**
     * Performs dexopt on all code paths and libraries of the specified package for specified
     * instruction sets.
     *
     * <p>Calls to {@link com.android.server.pm.Installer#dexopt} are synchronized on
     * {@link PackageManagerService#mInstallLock}.
     */
    int performDexOpt(PackageParser.Package pkg, String[] instructionSets,
            boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {
        ArraySet<String> done;
        if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {
            done = new ArraySet<String>();
            done.add(pkg.packageName);
        } else {
            done = null;
        }
        synchronized (mPackageManagerService.mInstallLock) {
            final boolean useLock = mSystemReady;
             // 除了在启动启动时段,其他时段 mSystemReady一般为true
            if (useLock) {
                mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
                mDexoptWakeLock.acquire();
            }
            try {
                // 核心方法
                return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);
            } finally {
                if (useLock) {
                    mDexoptWakeLock.release();
                }
            }
        }
    }

有注释,先来看下注释

对指定包内的代码和库执行dexopt。

方法内部主要用mInstallLock来加锁,然后调用performDexOptLI(PackageParser.Package, String[],String[], boolean, String,CompilerStats.PackageStats packageStats)方法

下面让我们来看下这个方法

1、performDexOptLI(PackageParser.Package, String[],String[], boolean, String,CompilerStats.PackageStats packageStats)方法解析

代码在PackageDexOptimizer.java 98行

    private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
            boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {
        final String[] instructionSets = targetInstructionSets != null ?
                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);

        if (done != null) {
            done.add(pkg.packageName);

            // 是否有一些共享库的apk,也要进行dex优化
            //usesLibraries 保存着AndroidManifest中的<uses-library>标签中android:required=true库
            if (pkg.usesLibraries != null) {
                //  进行dexopt优化 
                performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
                        bootComplete, done);
            }
            // usesOptionalLibraries 保存着AndroidManifest中<uses-library>标签中的 android:required=false的库
            if (pkg.usesOptionalLibraries != null) {
                 //  进行dexopt优化 
                performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
                        bootComplete, done);
            }
        }

        // 没有代码的包直接跳过
        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
            return DEX_OPT_SKIPPED;
        }

        // 是否是虚拟机的安全模式
        final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
        //  是否是debug模式
        final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        // 获取所有代码的路径
        final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
        boolean performedDexOpt = false;
        // There are three basic cases here:
        // 1.) we need to dexopt, either because we are forced or it is needed
        // 2.) we are deferring a needed dexopt
        // 3.) we are skipping an unneeded dexopt
        // 通过ArraySet 复制一份 数组
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        // 开始遍历 数组
        for (String dexCodeInstructionSet : dexCodeInstructionSets) {
            if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
                // 没有强制优化或者已经dex优化过 直接continue
                continue;
            }
            // 遍历所有 代码路径
            for (String path : paths) {
                final int dexoptNeeded;
                if (forceDex) {
                    // 如果是强制dex优化
                    dexoptNeeded = DexFile.DEX2OAT_NEEDED;
                } else {
                    try {
                        // 调用的DexFile的静态方法获取虚拟机对代码的优化意图
                        dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
                                dexCodeInstructionSet, defer);
                    } catch (IOException ioe) {
                        Slog.w(TAG, "IOException reading apk: " + path, ioe);
                        return DEX_OPT_FAILED;
                    }
                }
                // 如果不是强制优化且要求延迟优化,并且优化策略是不需要优化,则延迟优化
                if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                  
                    // We're deciding to defer a needed dexopt. Don't bother dexopting for other
                    // paths and instruction sets. We'll deal with them all together when we process
                    // our list of deferred dexopts.
                    // 把包放到延迟优化列表 内部是add到一个ArraySet中
                    addPackageForDeferredDexopt(pkg);
                     // 返回延迟优化
                    return DEX_OPT_DEFERRED;
                }
                 // 如果不是 没必要优化,则意味着要做优化
                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                    final String dexoptType;
                    String oatDir = null;
                   // 如果优化意图是dex->oat
                    if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
                        dexoptType = "dex2oat";
                        try {
                            // 获取 oat目录
                            oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
                        } catch (IOException ioe) {
                            Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName);
                            return DEX_OPT_FAILED;
                        }
                    } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
                        // 优化意图为 补丁优化
                        dexoptType = "patchoat";
                    } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
                        // 优化意图 为 用虚拟机的循环 补丁优化
                        dexoptType = "self patchoat";
                    } else {
                        throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
                    }

                    Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                            + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                            + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
                            + " oatDir = " + oatDir + " bootComplete=" + bootComplete);
                    // 获取sharedGid
                    final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
                    // 调用mPackageManagerService的mInstaller的dexopt方法来进行优化
                    final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
                            !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
                            dexoptNeeded, vmSafeMode, debuggable, oatDir, bootComplete);

                    // Dex2oat might fail due to compiler / verifier errors. We soldier on
                    // regardless, and attempt to interpret the app as a safety net.
                    if (ret == 0) {
                        // 优化成功
                        performedDexOpt = true;
                    }
                }
            }

            // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
            // either have either succeeded dexopt, or have had getDexOptNeeded tell us
            // it isn't required. We therefore mark that this package doesn't need dexopt unless
            // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
            // it.
            // 将他添加到已经优化过的缓存中
            pkg.mDexOptPerformed.add(dexCodeInstructionSet);
        }

        // If we've gotten here, we're sure that no error occurred and that we haven't
        // deferred dex-opt. We've either dex-opted one more paths or instruction sets or
        // we've skipped all of them because they are up to date. In both cases this
        // package doesn't need dexopt any longer.
        return performedDexOpt ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
    }

上面这个方法遍历了APK的所有代码路径,根据解析得到了dexoptType,最后用installd来完成了dexopt工作,其中如果dexoptType为dex2oat时,会调用createOatDirIfSupported方法获得oatdir。其他情况oatdir为空

createOatDirIfSupported方法很简单,用Install在该目录下创建一个目录。我就不详细讲解了

关于installd的dexopt工作内容,我会在后续讲解虚拟机的时候详细讲解。

六、args.doRename(res.returnCode, pkg, oldCodePath)方法解析

代码位置在PackageManagerService的installPackageLI方法里面会调用到,代码如下:
PackageManagerService.java 12461行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
        ...
    }

我们知道这里面的args指的是FileInstallArgs对象,所以args.doRename(res.returnCode, pkg, oldCodePath)方法就是FileInstallArgs#doRename(int, PackageParser.Package, String)方法

那我们就来看下FileInstallArgs#doRename(int, PackageParser.Package, String)方法
代码在PackageManagerService.java 11115行

        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
             // 如果没有成功,清理并返回改名事变
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                cleanUp();
                return false;
            }
             // 获取父目录
            final File targetDir = codeFile.getParentFile();
            // 获取旧的文件
            final File beforeCodeFile = codeFile;
            // 获取目录下的新的文件 调用getNextCodePath方法,后面会详解讲解
            final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);

            if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
            try {
                // 调用 Os的rename方法进行重命名
                Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
            } catch (ErrnoException e) {
                Slog.w(TAG, "Failed to rename", e);
                return false;
            }

            //  设置改名后文件的SELinux的上下文,后续会有在SELinux专题中详细讲解
            if (!SELinux.restoreconRecursive(afterCodeFile)) {
                Slog.w(TAG, "Failed to restorecon");
                return false;
            }

            // Reflect the rename internally
            codeFile = afterCodeFile;
            resourceFile = afterCodeFile;

            // Reflect the rename in scanned details
            // 重命名后一些变量也需要跟着变化
            pkg.codePath = afterCodeFile.getAbsolutePath();
            pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
                    pkg.baseCodePath);
            pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
                    pkg.splitCodePaths);

            // Reflect the rename in app info
            pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
            pkg.applicationInfo.setCodePath(pkg.codePath);
            pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
            pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
            pkg.applicationInfo.setResourcePath(pkg.codePath);
            pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
            pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);

            return true;
        }

上面这个方法,代码不多,我已经加上注释了,主要是就调用getNextCodePath方法来获取新的apk的目录名字,然后调用os的rename函数重命名,然后进行重命名后变量属性的变更。

这里面涉及到一个重要方法,即getNextCodePath(File,String)方法,我们来看一下
代码在PackageManagerService.java 11689行

    private File getNextCodePath(File targetDir, String packageName) {
        int suffix = 1;
        File result;
        do {
            result = new File(targetDir, packageName + "-" + suffix);
            suffix++;
        } while (result.exists());
        return result;
    }

代码很简单,就是获取一个新的APK目录名字在, 然后在APK的机上一个"-"+数字的后缀。

七、startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);方法解析

代码位置在PackageManagerService的installPackageLI方法里面会调用到,代码如下:
PackageManagerService.java 12466行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
        ...
    }

那我们就来看下PackageManagerService#startIntentFilterVerifications(int, boolean,PackageParser.Package)方法

代码在PackageManagerService.java 11115行

    private void startIntentFilterVerifications(int userId, boolean replacing,
            PackageParser.Package pkg) {
         // intentFilter的验证组件
        if (mIntentFilterVerifierComponent == null) {
            Slog.w(TAG, "No IntentFilter verification will not be done as "
                    + "there is no IntentFilterVerifier available!");
            return;
        }
  
        // 获取验证的uid
        final int verifierUid = getPackageUid(
                mIntentFilterVerifierComponent.getPackageName(),
                (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId); 
        // 删除what值为START_INTENT_FILTER_VERIFICATIONS的message,避免重复
        mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
        // 创建一个 what值为START_INTENT_FILTER_VERIFICATIONS的Message
        final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
        // 构造这个IFVerificationParams并把它赋值给Message的obj字段
        msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);
        // 发送一个Message
        mHandler.sendMessage(msg);
    }

这个方法内部主要是获取了一个Message对象,然后构造了一个IFVerificationParams,并且把这个IFVerificationParams对象指向了Message的obj。然后发送了这个Message对象。

我们先来看下IFVerificationParams这个类

1、IFVerificationParams类

代码在PackageManagerService.java 606行

    private static class IFVerificationParams {
        PackageParser.Package pkg;
        boolean replacing;
        int userId;
        int verifierUid;

        public IFVerificationParams(PackageParser.Package _pkg, boolean _replacing,
                int _userId, int _verifierUid) {
            pkg = _pkg;
            replacing = _replacing;
            userId = _userId;
            replacing = _replacing;
            verifierUid = _verifierUid;
        }
    }

哈哈,居然发现android 源码的一个问题,它给replacing赋值了两次

我们看到这个类其实就是一个包装类,包装了4个字段而已

IFVerificationParams类这个类看完,我们来看下what值为START_INTENT_FILTER_VERIFICATIONS的Message对应的处理逻辑

2、what值为START_INTENT_FILTER_VERIFICATIONS的Message对应的处理逻辑

代码在PackageManagerService.java 1582行

        void doHandleMessage(Message msg) {
            switch (msg.what) {
                ...
                case START_INTENT_FILTER_VERIFICATIONS: {
                    IFVerificationParams params = (IFVerificationParams) msg.obj;
                    verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
                            params.replacing, params.pkg);
                    break;
                }
                ...
      }

我们看到在case START_INTENT_FILTER_VERIFICATIONS里面就做了两件事:

  • 获取IFVerificationParams 对象params
  • 调用 verifyIntentFiltersIfNeeded(int, int,boolean,PackageParser.Package)方法

那我们来看下verifyIntentFiltersIfNeeded方法的内部执行情况

3、PackageManagerService#verifyIntentFiltersIfNeeded(int, int,boolean,PackageParser.Package)方法解析

代码在PackageManagerService.java 12501行

    private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
            PackageParser.Package pkg) {
         // 获取安装包中所有activity的数量
        int size = pkg.activities.size();
        // 如果没有activity则不需要验证,直接返回
        if (size == 0) {
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                    "No activity, so no need to verify any IntentFilter!");
            return;
        }
      
        // 判断 这个安装包内是否设置了url的过滤限制
        final boolean hasDomainURLs = hasDomainURLs(pkg);
        // 如果没有设置url过滤限制,则直接返回
        if (!hasDomainURLs) {
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                    "No domain URLs, so no need to verify any IntentFilter!");
            return;
        }

        if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
                + " if any IntentFilter from the " + size
                + " Activities needs verification ...");

        int count = 0;
        final String packageName = pkg.packageName;

        synchronized (mPackages) {
            // If this is a new install and we see that we've already run verification for this
            // package, we have nothing to do: it means the state was restored from backup.
            // 不是提前,即是新安装
            if (!replacing) {
                 // 如果是新安装,我们只需要判断是不是之前是不是验证,过
                IntentFilterVerificationInfo ivi =
                        mSettings.getIntentFilterVerificationLPr(packageName);
                if (ivi != null) {
                   // 如果ivi不为null,则意味着验证过,不需要继续验证了
                    if (DEBUG_DOMAIN_VERIFICATION) {
                        Slog.i(TAG, "Package " + packageName+ " already verified: status="
                                + ivi.getStatusString());
                    }
                    return;
                }
            }

            // If any filters need to be verified, then all need to be.
            //首先判断是否需要intent验证,即遍历所有的activity,判断每一个activity是否需要进行验证,
            //只要有一个需要验证,则需要进行验证, 如果一个都没有,则不需要验证
            boolean needToVerify = false;
            for (PackageParser.Activity a : pkg.activities) {
                for (ActivityIntentInfo filter : a.intents) {
                    if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
                        if (DEBUG_DOMAIN_VERIFICATION) {
                            Slog.d(TAG, "Intent filter needs verification, so processing all filters");
                        }
                        needToVerify = true;
                        break;
                    }
                }
            }

            // 如果需要验证
            if (needToVerify) {
                final int verificationId = mIntentFilterVerificationToken++;
                for (PackageParser.Activity a : pkg.activities) {
                    for (ActivityIntentInfo filter : a.intents) {
                        // 如果需要验证
                        if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
                            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                                    "Verification needed for IntentFilter:" + filter.toString());
                            // 把验证加入到mIntentFilterVerifier里面
                            mIntentFilterVerifier.addOneIntentFilterVerification(
                                    verifierUid, userId, verificationId, filter, packageName);
                             // 需要验证数量+1
                            count++;
                        }
                    }
                }
            }
        }

       // 如果验证数量大于0,开启验证
        if (count > 0) {
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
                    + " IntentFilter verification" + (count > 1 ? "s" : "")
                    +  " for userId:" + userId);
            mIntentFilterVerifier.startVerifications(userId);
        } else {
            if (DEBUG_DOMAIN_VERIFICATION) {
                Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
            }
        }
    }

这个方法内部首先判断这个安装包中的Activity的个数,如果一个Activity都没有,则不需要验证。然后用获取是否设置url验证。如果没设置,同样不需要验证。如果经历了前面的两重验证, 还没返回则说明activity的个数大于0,并且有url验证。这时候还要考虑一种情况,即新安装且已经检验过了。所以再进行判断是新安装且已经安装过的情况。最后开始遍历安装包的每一个activity,判断是否有验证的设置。如果连一个activity的验证设置都没有,则不需要验证。如果有验证设置则将验证设置添加到mIntentFilterVerifier中,并给count+1。最后如果count>0,则说明有验证设置,最后调用mIntentFilterVerifier.startVerifications(userId)这行代码进行验证

那我们就来看下mIntentFilterVerifier.startVerifications(userId)这行代码的内部执行逻辑

4、IntentFilterVerifier#startVerifications(int)方法解析

首先我们知道IntentFilterVerifier是一个接口
代码在PackageManagerService.java 622行

    private interface IntentFilterVerifier<T extends IntentFilter> {
        boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
                                               T filter, String packageName);
        void startVerifications(int userId);
        void receiveVerificationResponse(int verificationId);
    }

既然是一个接口,我们要找到它的具体实现类
PackageManagerService.java的构造函数里面有对mIntentFilterVerifier进行初始化

代码在PackageManagerService.java 2335行

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ...
            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                    mIntentFilterVerifierComponent);
            ...
    }

所以我们知道了mIntentFilterVerifier其实就是IntentVerifierProxy对象

那我们来看下IntentVerifierProxy的startVerifications(int)方法的具体实现
代码在PackageManagerService.java 644行

        @Override
        public void startVerifications(int userId) {
            // Launch verifications requests
            // 获取总体验证的数量
            int count = mCurrentIntentFilterVerifications.size();
            // 开始遍历
            for (int n=0; n<count; n++) {
                // 首先获取验证id
                int verificationId = mCurrentIntentFilterVerifications.get(n);
                // 根据验证id 获取对应的IntentFilter
                final IntentFilterVerificationState ivs =
                        mIntentFilterVerificationStates.get(verificationId);
                // 获取包名
                String packageName = ivs.getPackageName();
                 // 根据验证获取其对应的filters
                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
                // 获取filters的数量,方便后续的遍历
                final int filterCount = filters.size();
                // 创建ArraySet
                ArraySet<String> domainsSet = new ArraySet<>();
               // 开始遍历filters
                for (int m=0; m<filterCount; m++) {
                    // 获取其对应的具体某一个 PackageParser.ActivityIntentInfo
                    PackageParser.ActivityIntentInfo filter = filters.get(m);
                     // 把PackageParser.ActivityIntentInfo添加到domainsSet中
                    domainsSet.addAll(filter.getHostsList());
                }
                //把ArraySet转化为ArrayList
                ArrayList<String> domainsList = new ArrayList<>(domainsSet);
                synchronized (mPackages) {
                     // 根据包名获取其对应的PackageSetting,然后调用setIntentFilterVerificationInfo把其对应的IntentFilterVerificationInfo添加到PackageSetting中去
                    if (mSettings.createIntentFilterVerificationIfNeededLPw(
                            packageName, domainsList) != null) {
                        // 延迟写入
                        scheduleWriteSettingsLocked();
                    }
                }
                // 发送验证广播
                sendVerificationRequest(userId, verificationId, ivs);
            }
           // 清空
            mCurrentIntentFilterVerifications.clear();
        }

注释已经很清楚了,关注下最后的一个方法sendVerificationRequest方法

代码在PackageManagerService.java 673行

        private void sendVerificationRequest(int userId, int verificationId,
                IntentFilterVerificationState ivs) {

            Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
            verificationIntent.putExtra(
                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
                    verificationId);
            verificationIntent.putExtra(
                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
                    getDefaultScheme());
            verificationIntent.putExtra(
                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
                    ivs.getHostsString());
            verificationIntent.putExtra(
                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
                    ivs.getPackageName());
            verificationIntent.setComponent(mIntentFilterVerifierComponent);
            verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

            UserHandle user = new UserHandle(userId);
            mContext.sendBroadcastAsUser(verificationIntent, user);
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                    "Sending IntentFilter verification broadcast");
        }

我们看到这个方法里面什么也没做,就是发送了一个广播。由于广播内面牵扯的太多了,我就不再继续深入了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容