PackageManagerService 原始碼分析(2)
一.scanPackageLI
PKMS 中呼叫scanDirLI來分析APK 檔案,如果目錄下的是apk檔案或者是目錄,會繼續呼叫scanPackageLI函式:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { ... ... parseFlags |= mDefParseFlags; PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); pp.setDisplayMetrics(mMetrics); if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) { parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; } final PackageParser.Package pkg; try { pkg = pp.parsePackage(scanFile, parseFlags); } catch (PackageParserException e) { throw PackageManagerException.from(e); } ... ... }
這段程式碼主要作用是呼叫了PackageParser的parsePackage來解析檔案,返回Package;
二.PackageParser
PackageParser.parserPackage
public Package parsePackage(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { // 目錄 return parseClusterPackage(packageFile, flags); } else { //單個APK return parseMonolithicPackage(packageFile, flags); } }
parseClusterPackage
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException { final PackageLite lite = parseClusterPackageLite(packageDir, 0);//獲取應用目錄的PackageLite物件,這個物件分開儲存了目錄下的核心應用以及非核心應用的名稱 if (mOnlyCoreApps && !lite.coreApp) {//如果lite中沒有核心應用,退出 throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + packageDir); } final AssetManager assets = new AssetManager(); try { // Load the base and all splits into the AssetManager // so that resources can be overriden when parsing the manifests. loadApkIntoAssetManager(assets, lite.baseCodePath, flags);//裝載核心應用的資源 if (!ArrayUtils.isEmpty(lite.splitCodePaths)) { for (String path : lite.splitCodePaths) { loadApkIntoAssetManager(assets, path, flags);//再裝載非核心應用的資源 } } final File baseApk = new File(lite.baseCodePath); final Package pkg = parseBaseApk(baseApk, assets, flags);//對核心應用解析 ... ... if (!ArrayUtils.isEmpty(lite.splitNames)) { final int num = lite.splitNames.length; pkg.splitNames = lite.splitNames; pkg.splitCodePaths = lite.splitCodePaths; pkg.splitRevisionCodes = lite.splitRevisionCodes; pkg.splitFlags = new int[num]; for (int i = 0; i < num; i++) { parseSplitApk(pkg, i, assets, flags);//對非核心應用的處理 } } pkg.codePath = packageDir.getAbsolutePath(); return pkg; } finally { IoUtils.closeQuietly(assets); } }
parseClusterPackage的主要內容,就是用於解析存在多個APK的檔案的Package:
- 1. 呼叫parseClusterPackageLite解析目錄下的多APK檔案,獲取對應的PackageLite物件lite;
- 2. 建立AssetManager物件,並呼叫loadApkIntoAssetManager方法載入"base APK"
- 3. 呼叫parseBaseApk方法獲取對應的Package物件
- 4. 遍歷所有"拆分APK",然後載入第二步建立的AssetManager物件,這樣就實現了資原始檔的載入
parseMonolithicPackage
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//如果是核心應用則以更輕量級的方式進行解析後,判斷是否是核心應用,非核心應用不執行解析過程
if (mOnlyCoreApps) {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
//呼叫parseBaseAPK()繼續解析操作
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
解析操作繼續由parseBaseApk(apkFile, assets, flags)
完成,parseBaseApk(apkFile, assets, flags)最終呼叫同名函式:
parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError)
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
//....省略多行程式碼....
//迴圈解析AndroidManifest.xml中的元素
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals("application")) {
//....省略多行程式碼,重點關注其中呼叫的parseBaseApplication()方法
}else if (tagName.equals("overlay")) {
//....省略多行程式碼....
}else if (tagName.equals("key-sets")){
//paseKeySets()
}else if (tagName.equals("permission-group")) {
parsePermissionGroup(pkg, flags, res, parser, attrs,
}else if (tagName.equals("permission")) {
//parsePermission
}else if (tagName.equals("uses-configuration")) {
//....省略多行程式碼....
}else if (tagName.equals("uses-feature")) {
//parseUsesFeature()
}else if (tagName.equals("feature-group")) {
//....省略多行程式碼....
}else if (tagName.equals("uses-sdk")) {
//....省略多行程式碼....
}else if (tagName.equals("supports-screens")) {
//....省略多行程式碼....
}else if (tagName.equals("protected-broadcast")) {
//....省略多行程式碼....
}else if (tagName.equals("instrumentation")) {
//....省略多行程式碼....
}else if (tagName.equals("original-package")) {
//....省略多行程式碼....
}else if (tagName.equals("adopt-permissions")) {
//....省略多行程式碼....
}
//....省略多行程式碼....
}
//....省略多汗程式碼....
}
此函式就是解析APK 的AndroidManifest.xml 檔案;
所以,PackageParser.parsePackage() 函式的作用可以簡單的看成完成了 dir 下對應APK 的AndroidManifest.xml 的解析,儲存在package 物件中並返回;
- scanPackageLI(File scanFile,...)該函式負責完成APK的掃描工作,APK的掃描工作具體有PackageParser.parsePackage(*)完成。在掃描過程中,會解析AndroidManifiest.xml檔案等資訊,並將掃描結果存放在PackageParser.Package物件中。
- 在完成包的資訊解析之後需要完成包資訊同步工作,主要因為:scanPackageLI掃描到的APK可能是已經更名的包、disable的包、需要升級的包、已經安裝並且簽名衝突的包、被非系統級包替代系統包的情況,需要對這些情況一一處理,保證資訊的正確性。
- 如果Package需要Rename或者Update則會進行簽名比較,以防止簽名不一致的情況;
- 最終會呼叫scanPackageLI(PackageParser.Package,....)函式完成實際的Package安裝或更新操作。
- 在掃描過程中,Package的Rename、Update、Install操作都是由scanPackage(PackageParser,int,int,long,UserHandler)來完成.該函式會呼叫scanPackageDirtyLI(...)完成具體的操作
scanPackageDirtyLI
需要注意的是:不論是開機掃描安裝APK,還是通過adb命令安裝APK,最終都會呼叫scanPackageDirtyLI函式進行APK安裝
總結一下:
1 .任一時刻,系統中安裝過的apk都會被解析完成,包資訊被存放在PackageManagerService中的mPackages,它是以包名為Key,以PackageParser.Package為Value的HashMap。
2 .PMS中scanPackageLI()開機掃描已安裝的apk,installPackageLI()安裝新apk,最終都會走到PackageParser.parseBaseApplication()解析應用manifest檔案,在scanPackageDirtyLI()中新增到mPackages.其它地方獲取ApplicationInfo例項,都會間接地呼叫到PackageParser.generateApplicationInfo().