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 對象中並返回;

 

  1. scanPackageLI(File scanFile,...)該函數負責完成APK的掃描工作,APK的掃描工作具體有PackageParser.parsePackage(*)完成。在掃描過程中,會解析AndroidManifiest.xml文件等信息,並將掃描結果存放在PackageParser.Package對象中。
  2. 在完成包的信息解析之後需要完成包信息同步工作,主要因爲:scanPackageLI掃描到的APK可能是已經更名的包、disable的包、需要升級的包、已經安裝並且簽名衝突的包、被非系統級包替代系統包的情況,需要對這些情況一一處理,保證信息的正確性。
  3. 如果Package需要Rename或者Update則會進行簽名比較,以防止簽名不一致的情況;
  4. 最終會調用scanPackageLI(PackageParser.Package,....)函數完成實際的Package安裝或更新操作。
  5. 在掃描過程中,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().

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章