APK安裝流程系列文章整體內容如下:
- APK安裝流程詳解0——前言
- APK安裝流程詳解1——有關"安裝ing"的實體類概述
- APK安裝流程詳解2——PackageManager簡介
- APK安裝流程詳解3——PackageManager與PackageManagerService
- APK安裝流程詳解4——安裝中關于so庫的哪些事
- APK安裝流程詳解5——PackageInstallerService和Installer
- APK安裝流程詳解6——PackageManagerService啟動前奏
- APK安裝流程詳解7——PackageManagerService的啟動流程(上)
- APK安裝流程詳解8——PackageManagerService的啟動流程(下)
- APK安裝流程詳解9——PackageParser解析APK(上)
- APK安裝流程詳解10——PackageParser解析APK(下)
- APK安裝流程詳解11——普通應用安裝簡介
- APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
- APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)
- APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充
- APK安裝流程詳解15——PMS中的新安裝流程下(裝載)補充
- APK安裝流程詳解16——Android包管理總結(尚未完結請期待)
本片文章的主要內容如下:
- 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");
}
我們看到這個方法里面什么也沒做,就是發送了一個廣播。由于廣播內面牽扯的太多了,我就不再繼續深入了。