APK安裝流程系列文章整體內(nèi)容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關(guān)"安裝ing"的實(shí)體類概述
- APK安裝流程詳解2——PackageManager簡(jiǎn)介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關(guān)于so庫(kù)的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動(dòng)前奏
- APK安裝流程詳解7——PackageManagerService的啟動(dòng)流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應(yīng)用安裝簡(jiǎn)介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補(bǔ)充
- APK安裝流程詳解16——Android包管理總結(jié)(尚未完結(jié)請(qǐng)期待)
本片文章的主要內(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ù)深入了。