APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充

APK安裝流程系列文章整體內(nèi)容如下:

本片文章的主要內(nèi)容如下:

  • 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方法里面會(huì)調(diào)用到,代碼如下:
PackageManagerService.java

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

可以看到阔籽,這里構(gòu)造了一個(gè)PackageParser對(duì)象流妻,然后設(shè)置了mSeparateProcesses屬性。

mSeparateProcesses是一個(gè)數(shù)組笆制,表示獨(dú)立的進(jìn)程名列表绅这,這個(gè)參數(shù)是在PackageManagerService的構(gòu)造函數(shù)中調(diào)用到,以后會(huì)分析一下函數(shù)是在什么地方調(diào)用在辆,所以看mSeparateProcesses的獲取過(guò)程:

代碼在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;
        }    

從系統(tǒng)屬性中讀取debug.separate_processes屬性证薇,如果改屬性返回值不為空,表示設(shè)置了該屬性开缎,否則系統(tǒng)未設(shè)置改屬性棕叫,如果值等于則mSeparateProcesses為空,如果不為奕删,則逗號(hào)分隔該字符串俺泣,解析每個(gè)獨(dú)立的進(jìn)程名,那個(gè)debug.separate_processes究竟有什么用?

那我們就來(lái)看下debug.separate_processes的作用:

separate_processes可以讓?xiě)?yīng)用程序的組件運(yùn)行在自己的進(jìn)程里面伏钠,separate_processes一般有兩種設(shè)置:

  • 如果設(shè)置了"setprop debug.separate_processes",則將設(shè)置這個(gè)每個(gè)包中的每個(gè)進(jìn)程横漏。
  • 如果設(shè)置"setprop debug.separate_processes 'com.google.process.content,com.google.android.samples' "它只會(huì)影響項(xiàng)目清單中的指定進(jìn)程("com.google.process.content,com.google.android.samples")∈斓啵或者在AndroidManifest里面顯式的設(shè)置"android:process"標(biāo)記缎浇。
PS:雖然這樣可以將一個(gè)進(jìn)程拆分出來(lái)岗喉,或者多個(gè)進(jìn)程組合成一個(gè)進(jìn)程(他們必須來(lái)自同一個(gè)包)陨帆。它會(huì)強(qiáng)制所有受影響的組件在自己的.apk運(yùn)行。

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

這個(gè)方法在PackageManagerService的installPackageLI方法里面被調(diào)用誉券。代碼在代碼在PackageManagerService.java 12346行

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

那我們來(lái)看下shouldCheckUpgradeKeySetLP這個(gè)方法的內(nèi)部實(shí)現(xiàn)
代碼在PackageManagerService.java 11807行

    private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
        // Can't rotate keys during boot or if sharedUser.
         // 判斷是否可以進(jìn)行升級(jí)驗(yàn)證的條件
        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數(shù)組
        long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();
        for (int i = 0; i < upgradeKeySets.length; i++) {
            // 遍歷keySet數(shù)組指厌,檢查是否有對(duì)應(yīng)的密鑰集
            if (!ksms.isIdValidKeySetId(upgradeKeySets[i])) {
                // 如果對(duì)應(yīng)的密鑰集合,說(shuō)明簽名密鑰有問(wèn)題踊跟,則返回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;
            }
        }
     // 如果所有的密鑰都能對(duì)上踩验,說(shuō)明密鑰沒(méi)有問(wèn)題,則返回true
        return true;
    }

通過(guò)注釋我們知道商玫,方法主要檢查密鑰集合是否和老版本的一致箕憾,如果不一致,則返回false拳昌。如果一致則返回true袭异。

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

這個(gè)方法在PackageManagerService的installPackageLI方法里面被調(diào)用炬藤。代碼在代碼在PackageManagerService.java 12347行

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

那我們來(lái)看下checkUpgradeKeySetLP這個(gè)方法的內(nèi)部實(shí)現(xiàn)
代碼在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.
        // 如果升級(jí)KeySet扁远,確保新的安裝包是否有超集的keys
        // 獲取舊版本的KeySet數(shù)組
        long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
        KeySetManagerService ksms = mSettings.mKeySetManagerService;
        // 遍歷KeySet數(shù)組
        for (int i = 0; i < upgradeKeySets.length; i++) {
             // 根據(jù)密鑰獲取公鑰
            Set<PublicKey> upgradeSet = ksms.getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
            if (upgradeSet != null && newPkg.mSigningKeys.containsAll(upgradeSet)) {
                 // 如果對(duì)應(yīng)上 則返回true,
                return true;
            }
        }
        // 遍歷都沒(méi)有符合的刻像,則返回false
        return false;
    }

這個(gè)方法內(nèi)部主要檢查是否有匹配的公鑰畅买,如果有則返回true,沒(méi)有則返回false细睡。

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

這個(gè)方法在PackageManagerService的installPackageLI方法里面被調(diào)用。代碼在PackageManagerService.java 12355行

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

那我們來(lái)看下verifySignaturesLP這個(gè)方法的內(nèi)部實(shí)現(xiàn)
代碼在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) {
           // 如果有共享用戶湃缎,則檢驗(yàn)共享用戶的簽名
            // 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!");
            }
        }

這個(gè)方法其實(shí)很簡(jiǎn)單,分為兩個(gè)部分

  • 如果有老版本的簽名 則檢查老版本的簽名和新安裝包的簽名是否一致
  • 如果有共享用戶的簽名蠢壹,則檢查共享用戶的簽名與新安裝包的簽名是否一致嗓违。

里面驗(yàn)證簽名都是三重機(jī)制,如下:

  • 第一重校驗(yàn):調(diào)用compareSignatures方法图贸,比較就的APK的簽名和新的APK簽名是否相同蹂季,如果返回值是PackageManager.SIGNATURE_MATCH冕广,則通過(guò)并且不用后續(xù)校驗(yàn),沒(méi)有通過(guò)則進(jìn)行第二重校驗(yàn)偿洁。
  • 第二重校驗(yàn):調(diào)用compareSignaturesCompat方法撒汉,比較就的APK的簽名和新的APK簽名是否相同,如果返回值是PackageManager.SIGNATURE_MATCH涕滋,則通過(guò)并且不進(jìn)行后續(xù)校驗(yàn)睬辐,沒(méi)有通過(guò)則進(jìn)行第二重校驗(yàn)。
  • 第三重校驗(yàn):調(diào)用compareSignaturesRecover方法宾肺,比較舊的APK的簽名和新的APK簽名是否相同溯饵,如果返回值是PackageManager.SIGNATURE_MATCH,則通過(guò)并且不進(jìn)行后續(xù)校驗(yàn)锨用,沒(méi)有通過(guò)則拋出異常瓣喊,結(jié)束執(zhí)行。

那我們就依次看下這三個(gè)方法

(一)黔酥、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;
    }

上代碼很簡(jiǎn)單就是先做非空判斷,然后把兩個(gè)數(shù)組轉(zhuǎn)化成ArraySet洪橘,然后判斷兩個(gè)ArraySet是否相同跪者,如果相同則返回PackageManager.SIGNATURE_MATCH,如果不相同則返回PackageManager.SIGNATURE_NO_MATCH

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

如果上面的匹配不符合則說(shuō)明當(dāng)前不匹配渣玲,所我們要考慮是不是版本的的問(wèn)題,所以就有了這個(gè)方法弟晚。

代碼在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) {
        // 第一步
        // 更新安裝包名的簽名版本是否小于數(shù)據(jù)庫(kù)中簽名版本
        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;
    }

先來(lái)看下注釋:

這個(gè)方法主要是保證向后的兼容性,這樣可以確保證書(shū)鏈上的包可以升級(jí)到最新的版本卿城。existingSigs是舊格式枚钓,因?yàn)樗谏?jí)前是在磁盤(pán)空間上,scansSigs是新的格式瑟押。

我將這個(gè)方法分為三個(gè)步驟

  • 第一步:判斷升級(jí)包的簽名版本是否小于當(dāng)前系統(tǒng)中簽名的數(shù)據(jù)庫(kù)版本號(hào)搀捷,上面一層判斷已經(jīng)不匹配才會(huì)走到這個(gè)方法里面,所以如果更新的安裝包的簽名版本大于當(dāng)前數(shù)據(jù)庫(kù)中的簽名版本號(hào)多望,則一定是不匹配的嫩舟。所以會(huì)返回PackageManager.SIGNATURE_NO_MATCH
  • 第二步:同樣是通過(guò)遍歷的方式把舊的簽名數(shù)組轉(zhuǎn)化為ArraySet對(duì)象existingSet,同時(shí)遍歷新的安裝包中每個(gè)簽名的簽名鏈怀偷,并把簽名鏈加入到ArraySet對(duì)象
  • 第三步:確保擴(kuò)展的掃描集包含現(xiàn)有的所有簽名家厌。如果scannedCompatSet和existingSet一致,則進(jìn)行簽名替換椎工,并且在mSettings.mKeySetManagerService刪除簽名饭于。

PS:上文說(shuō)的DatabaseVersion 其實(shí)是Settings.java的內(nèi)部類

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

如果上面兩個(gè)匹配規(guī)則都沒(méi)有匹配,我們考慮是不是在證書(shū)有過(guò)變動(dòng)導(dǎo)致的匹配失敗镰绎,所以這個(gè)方法主要考慮是否恢復(fù)證書(shū)進(jìn)行匹配
代碼在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;
    }

我將上面的方法分為兩步:

  • 第一步:調(diào)用isCompatSignatureUpdateNeeded判斷是否有恢復(fù)的需求脓斩,這里隨帶說(shuō)下isCompatSignatureUpdateNeeded方法。Android LOLLIPOP這個(gè)版本是一個(gè)時(shí)間窗口畴栖,會(huì)對(duì)證書(shū)進(jìn)行修該随静。如果不用進(jìn)行證書(shū)恢復(fù),則整個(gè)這個(gè)方法就無(wú)意義了吗讶,直接返回PackageManager.SIGNATURE_NO_MATCH
  • 第二步:調(diào)用Signature的靜態(tài)方法areEffectiveMatch進(jìn)行匹配燎猛,這個(gè)方法內(nèi)部。由于在極少數(shù)情況下照皆,證書(shū)可能會(huì)有錯(cuò)誤的編碼重绷,導(dǎo)致匹配失敗。這個(gè)方法就是避免這種情況的解決方案膜毁,代碼不多昭卓,大家有興趣可以自行查看。

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

代碼位置在PackageManagerService的installPackageLI方法里面會(huì)調(diào)用到候醒,代碼如下:
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 */);
        ...
    }

那我們就來(lái)看下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;
             // 除了在啟動(dòng)啟動(dòng)時(shí)段,其他時(shí)段 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();
                }
            }
        }
    }

有注釋杂瘸,先來(lái)看下注釋

對(duì)指定包內(nèi)的代碼和庫(kù)執(zhí)行dexopt倒淫。

方法內(nèi)部主要用mInstallLock來(lái)加鎖,然后調(diào)用performDexOptLI(PackageParser.Package, String[],String[], boolean, String,CompilerStats.PackageStats packageStats)方法

下面讓我們來(lái)看下這個(gè)方法

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);

            // 是否有一些共享庫(kù)的apk敌土,也要進(jìn)行dex優(yōu)化
            //usesLibraries 保存著AndroidManifest中的<uses-library>標(biāo)簽中android:required=true庫(kù)
            if (pkg.usesLibraries != null) {
                //  進(jìn)行dexopt優(yōu)化 
                performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,
                        bootComplete, done);
            }
            // usesOptionalLibraries 保存著AndroidManifest中<uses-library>標(biāo)簽中的 android:required=false的庫(kù)
            if (pkg.usesOptionalLibraries != null) {
                 //  進(jìn)行dexopt優(yōu)化 
                performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,
                        bootComplete, done);
            }
        }

        // 沒(méi)有代碼的包直接跳過(guò)
        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {
            return DEX_OPT_SKIPPED;
        }

        // 是否是虛擬機(jī)的安全模式
        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
        // 通過(guò)ArraySet 復(fù)制一份 數(shù)組
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        // 開(kāi)始遍歷 數(shù)組
        for (String dexCodeInstructionSet : dexCodeInstructionSets) {
            if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {
                // 沒(méi)有強(qiáng)制優(yōu)化或者已經(jīng)dex優(yōu)化過(guò) 直接continue
                continue;
            }
            // 遍歷所有 代碼路徑
            for (String path : paths) {
                final int dexoptNeeded;
                if (forceDex) {
                    // 如果是強(qiáng)制dex優(yōu)化
                    dexoptNeeded = DexFile.DEX2OAT_NEEDED;
                } else {
                    try {
                        // 調(diào)用的DexFile的靜態(tài)方法獲取虛擬機(jī)對(duì)代碼的優(yōu)化意圖
                        dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,
                                dexCodeInstructionSet, defer);
                    } catch (IOException ioe) {
                        Slog.w(TAG, "IOException reading apk: " + path, ioe);
                        return DEX_OPT_FAILED;
                    }
                }
                // 如果不是強(qiáng)制優(yōu)化且要求延遲優(yōu)化,并且優(yōu)化策略是不需要優(yōu)化运翼,則延遲優(yōu)化
                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.
                    // 把包放到延遲優(yōu)化列表 內(nèi)部是add到一個(gè)ArraySet中
                    addPackageForDeferredDexopt(pkg);
                     // 返回延遲優(yōu)化
                    return DEX_OPT_DEFERRED;
                }
                 // 如果不是 沒(méi)必要優(yōu)化返干,則意味著要做優(yōu)化
                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                    final String dexoptType;
                    String oatDir = null;
                   // 如果優(yōu)化意圖是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) {
                        // 優(yōu)化意圖為 補(bǔ)丁優(yōu)化
                        dexoptType = "patchoat";
                    } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
                        // 優(yōu)化意圖 為 用虛擬機(jī)的循環(huán) 補(bǔ)丁優(yōu)化
                        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);
                    // 調(diào)用mPackageManagerService的mInstaller的dexopt方法來(lái)進(jìn)行優(yōu)化
                    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) {
                        // 優(yōu)化成功
                        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.
            // 將他添加到已經(jīng)優(yōu)化過(guò)的緩存中
            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;
    }

上面這個(gè)方法遍歷了APK的所有代碼路徑,根據(jù)解析得到了dexoptType血淌,最后用installd來(lái)完成了dexopt工作犬金,其中如果dexoptType為dex2oat時(shí),會(huì)調(diào)用createOatDirIfSupported方法獲得oatdir六剥。其他情況oatdir為空

createOatDirIfSupported方法很簡(jiǎn)單晚顷,用Install在該目錄下創(chuàng)建一個(gè)目錄。我就不詳細(xì)講解了

關(guān)于installd的dexopt工作內(nèi)容疗疟,我會(huì)在后續(xù)講解虛擬機(jī)的時(shí)候詳細(xì)講解该默。

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

代碼位置在PackageManagerService的installPackageLI方法里面會(huì)調(diào)用到策彤,代碼如下:
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對(duì)象栓袖,所以args.doRename(res.returnCode, pkg, oldCodePath)方法就是FileInstallArgs#doRename(int, PackageParser.Package, String)方法

那我們就來(lái)看下FileInstallArgs#doRename(int, PackageParser.Package, String)方法
代碼在PackageManagerService.java 11115行

        boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {
             // 如果沒(méi)有成功匣摘,清理并返回改名事變
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                cleanUp();
                return false;
            }
             // 獲取父目錄
            final File targetDir = codeFile.getParentFile();
            // 獲取舊的文件
            final File beforeCodeFile = codeFile;
            // 獲取目錄下的新的文件 調(diào)用getNextCodePath方法,后面會(huì)詳解講解
            final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);

            if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
            try {
                // 調(diào)用 Os的rename方法進(jìn)行重命名
                Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
            } catch (ErrnoException e) {
                Slog.w(TAG, "Failed to rename", e);
                return false;
            }

            //  設(shè)置改名后文件的SELinux的上下文裹刮,后續(xù)會(huì)有在SELinux專題中詳細(xì)講解
            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;
        }

上面這個(gè)方法音榜,代碼不多,我已經(jīng)加上注釋了捧弃,主要是就調(diào)用getNextCodePath方法來(lái)獲取新的apk的目錄名字赠叼,然后調(diào)用os的rename函數(shù)重命名,然后進(jìn)行重命名后變量屬性的變更违霞。

這里面涉及到一個(gè)重要方法嘴办,即getNextCodePath(File,String)方法,我們來(lái)看一下
代碼在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;
    }

代碼很簡(jiǎn)單买鸽,就是獲取一個(gè)新的APK目錄名字在涧郊, 然后在APK的機(jī)上一個(gè)"-"+數(shù)字的后綴。

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

代碼位置在PackageManagerService的installPackageLI方法里面會(huì)調(diào)用到妆艘,代碼如下:
PackageManagerService.java 12466行

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

那我們就來(lái)看下PackageManagerService#startIntentFilterVerifications(int, boolean,PackageParser.Package)方法

代碼在PackageManagerService.java 11115行

    private void startIntentFilterVerifications(int userId, boolean replacing,
            PackageParser.Package pkg) {
         // intentFilter的驗(yàn)證組件
        if (mIntentFilterVerifierComponent == null) {
            Slog.w(TAG, "No IntentFilter verification will not be done as "
                    + "there is no IntentFilterVerifier available!");
            return;
        }
  
        // 獲取驗(yàn)證的uid
        final int verifierUid = getPackageUid(
                mIntentFilterVerifierComponent.getPackageName(),
                (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId); 
        // 刪除what值為START_INTENT_FILTER_VERIFICATIONS的message,避免重復(fù)
        mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
        // 創(chuàng)建一個(gè) what值為START_INTENT_FILTER_VERIFICATIONS的Message
        final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
        // 構(gòu)造這個(gè)IFVerificationParams并把它賦值給Message的obj字段
        msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);
        // 發(fā)送一個(gè)Message
        mHandler.sendMessage(msg);
    }

這個(gè)方法內(nèi)部主要是獲取了一個(gè)Message對(duì)象看幼,然后構(gòu)造了一個(gè)IFVerificationParams批旺,并且把這個(gè)IFVerificationParams對(duì)象指向了Message的obj。然后發(fā)送了這個(gè)Message對(duì)象桌吃。

我們先來(lái)看下IFVerificationParams這個(gè)類

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;
        }
    }

哈哈苞轿,居然發(fā)現(xiàn)android 源碼的一個(gè)問(wèn)題茅诱,它給replacing賦值了兩次

我們看到這個(gè)類其實(shí)就是一個(gè)包裝類,包裝了4個(gè)字段而已

IFVerificationParams類這個(gè)類看完搬卒,我們來(lái)看下what值為START_INTENT_FILTER_VERIFICATIONS的Message對(duì)應(yīng)的處理邏輯

2瑟俭、what值為START_INTENT_FILTER_VERIFICATIONS的Message對(duì)應(yīng)的處理邏輯

代碼在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 對(duì)象params
  • 調(diào)用 verifyIntentFiltersIfNeeded(int, int,boolean,PackageParser.Package)方法

那我們來(lái)看下verifyIntentFiltersIfNeeded方法的內(nèi)部執(zhí)行情況

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

代碼在PackageManagerService.java 12501行

    private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
            PackageParser.Package pkg) {
         // 獲取安裝包中所有activity的數(shù)量
        int size = pkg.activities.size();
        // 如果沒(méi)有activity則不需要驗(yàn)證契邀,直接返回
        if (size == 0) {
            if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                    "No activity, so no need to verify any IntentFilter!");
            return;
        }
      
        // 判斷 這個(gè)安裝包內(nèi)是否設(shè)置了url的過(guò)濾限制
        final boolean hasDomainURLs = hasDomainURLs(pkg);
        // 如果沒(méi)有設(shè)置url過(guò)濾限制摆寄,則直接返回
        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) {
                 // 如果是新安裝坯门,我們只需要判斷是不是之前是不是驗(yàn)證微饥,過(guò)
                IntentFilterVerificationInfo ivi =
                        mSettings.getIntentFilterVerificationLPr(packageName);
                if (ivi != null) {
                   // 如果ivi不為null,則意味著驗(yàn)證過(guò)古戴,不需要繼續(xù)驗(yàn)證了
                    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驗(yàn)證欠橘,即遍歷所有的activity,判斷每一個(gè)activity是否需要進(jìn)行驗(yàn)證现恼,
            //只要有一個(gè)需要驗(yàn)證肃续,則需要進(jìn)行驗(yàn)證黍檩, 如果一個(gè)都沒(méi)有,則不需要驗(yàn)證
            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;
                    }
                }
            }

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

       // 如果驗(yàn)證數(shù)量大于0始锚,開(kāi)啟驗(yàn)證
        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);
            }
        }
    }

這個(gè)方法內(nèi)部首先判斷這個(gè)安裝包中的Activity的個(gè)數(shù)刽酱,如果一個(gè)Activity都沒(méi)有,則不需要驗(yàn)證瞧捌。然后用獲取是否設(shè)置url驗(yàn)證棵里。如果沒(méi)設(shè)置,同樣不需要驗(yàn)證察郁。如果經(jīng)歷了前面的兩重驗(yàn)證衍慎, 還沒(méi)返回則說(shuō)明activity的個(gè)數(shù)大于0,并且有url驗(yàn)證皮钠。這時(shí)候還要考慮一種情況稳捆,即新安裝且已經(jīng)檢驗(yàn)過(guò)了。所以再進(jìn)行判斷是新安裝且已經(jīng)安裝過(guò)的情況麦轰。最后開(kāi)始遍歷安裝包的每一個(gè)activity乔夯,判斷是否有驗(yàn)證的設(shè)置。如果連一個(gè)activity的驗(yàn)證設(shè)置都沒(méi)有款侵,則不需要驗(yàn)證末荐。如果有驗(yàn)證設(shè)置則將驗(yàn)證設(shè)置添加到mIntentFilterVerifier中,并給count+1新锈。最后如果count>0甲脏,則說(shuō)明有驗(yàn)證設(shè)置,最后調(diào)用mIntentFilterVerifier.startVerifications(userId)這行代碼進(jìn)行驗(yàn)證

那我們就來(lái)看下mIntentFilterVerifier.startVerifications(userId)這行代碼的內(nèi)部執(zhí)行邏輯

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

首先我們知道IntentFilterVerifier是一個(gè)接口
代碼在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);
    }

既然是一個(gè)接口块请,我們要找到它的具體實(shí)現(xiàn)類
PackageManagerService.java的構(gòu)造函數(shù)里面有對(duì)mIntentFilterVerifier進(jìn)行初始化

代碼在PackageManagerService.java 2335行

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

所以我們知道了mIntentFilterVerifier其實(shí)就是IntentVerifierProxy對(duì)象

那我們來(lái)看下IntentVerifierProxy的startVerifications(int)方法的具體實(shí)現(xiàn)
代碼在PackageManagerService.java 644行

        @Override
        public void startVerifications(int userId) {
            // Launch verifications requests
            // 獲取總體驗(yàn)證的數(shù)量
            int count = mCurrentIntentFilterVerifications.size();
            // 開(kāi)始遍歷
            for (int n=0; n<count; n++) {
                // 首先獲取驗(yàn)證id
                int verificationId = mCurrentIntentFilterVerifications.get(n);
                // 根據(jù)驗(yàn)證id 獲取對(duì)應(yīng)的IntentFilter
                final IntentFilterVerificationState ivs =
                        mIntentFilterVerificationStates.get(verificationId);
                // 獲取包名
                String packageName = ivs.getPackageName();
                 // 根據(jù)驗(yàn)證獲取其對(duì)應(yīng)的filters
                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
                // 獲取filters的數(shù)量,方便后續(xù)的遍歷
                final int filterCount = filters.size();
                // 創(chuàng)建ArraySet
                ArraySet<String> domainsSet = new ArraySet<>();
               // 開(kāi)始遍歷filters
                for (int m=0; m<filterCount; m++) {
                    // 獲取其對(duì)應(yīng)的具體某一個(gè) PackageParser.ActivityIntentInfo
                    PackageParser.ActivityIntentInfo filter = filters.get(m);
                     // 把PackageParser.ActivityIntentInfo添加到domainsSet中
                    domainsSet.addAll(filter.getHostsList());
                }
                //把ArraySet轉(zhuǎn)化為ArrayList
                ArrayList<String> domainsList = new ArrayList<>(domainsSet);
                synchronized (mPackages) {
                     // 根據(jù)包名獲取其對(duì)應(yīng)的PackageSetting拳缠,然后調(diào)用setIntentFilterVerificationInfo把其對(duì)應(yīng)的IntentFilterVerificationInfo添加到PackageSetting中去
                    if (mSettings.createIntentFilterVerificationIfNeededLPw(
                            packageName, domainsList) != null) {
                        // 延遲寫(xiě)入
                        scheduleWriteSettingsLocked();
                    }
                }
                // 發(fā)送驗(yàn)證廣播
                sendVerificationRequest(userId, verificationId, ivs);
            }
           // 清空
            mCurrentIntentFilterVerifications.clear();
        }

注釋已經(jīng)很清楚了墩新,關(guān)注下最后的一個(gè)方法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");
        }

我們看到這個(gè)方法里面什么也沒(méi)做,就是發(fā)送了一個(gè)廣播窟坐。由于廣播內(nèi)面牽扯的太多了海渊,我就不再繼續(xù)深入了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哲鸳,一起剝皮案震驚了整個(gè)濱河市臣疑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙菠,老刑警劉巖朝捆,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異懒豹,居然都是意外死亡芙盘,警方通過(guò)查閱死者的電腦和手機(jī)驯用,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)儒老,“玉大人蝴乔,你說(shuō)我怎么就攤上這事⊥苑” “怎么了薇正?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)囚衔。 經(jīng)常有香客問(wèn)我挖腰,道長(zhǎng),這世上最難降的妖魔是什么练湿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任猴仑,我火速辦了婚禮,結(jié)果婚禮上肥哎,老公的妹妹穿的比我還像新娘辽俗。我一直安慰自己,他們只是感情好篡诽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布崖飘。 她就那樣靜靜地躺著,像睡著了一般杈女。 火紅的嫁衣襯著肌膚如雪朱浴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,785評(píng)論 1 290
  • 那天达椰,我揣著相機(jī)與錄音翰蠢,去河邊找鬼。 笑死砰碴,一個(gè)胖子當(dāng)著我的面吹牛躏筏,可吹牛的內(nèi)容都是我干的板丽。 我是一名探鬼主播呈枉,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼埃碱!你這毒婦竟也來(lái)了猖辫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤砚殿,失蹤者是張志新(化名)和其女友劉穎啃憎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體似炎,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辛萍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年悯姊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贩毕。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悯许,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辉阶,到底是詐尸還是另有隱情先壕,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布谆甜,位于F島的核電站垃僚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏规辱。R本人自食惡果不足惜谆棺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望按摘。 院中可真熱鬧包券,春花似錦、人聲如沸炫贤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兰珍。三九已至侍郭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掠河,已是汗流浹背亮元。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唠摹,地道東北人爆捞。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像勾拉,于是被迫代替她去往敵國(guó)和親煮甥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容