APK安裝流程詳解9——PackageParser解析APK(上)

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

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

  • 1、PackageParser類簡介
  • 2、PackageParser類的結(jié)構(gòu)
  • 3、PackageParser類的內(nèi)部類簡介
  • 4、PackageParse#parsePackage(File, int)方法解析
  • 5、PackageParse#parseMonolithicPackage(File, int)方法解析
  • 6、PackageParse#parseMonolithicPackageLite(File, int)方法解析
  • 7、PackageParse#parseApkLite(File,int)方法解析
  • 8、PackageParse#parseApkLite(File,int)方法解析
  • 9、PackageParse#parsePackageSplitNames(XmlpullParse,AttributeSet,int)方法解析
  • 10、PackageParse#parseBaseApk(File,AssetManager,int)方法解析
  • 11、PackageParse#parseBaseApk(Resources,XmlResourceParser,int,String[])方法解析

一 、PackageParser類簡介

PackageParser.java

Android 安裝一個(gè)APK的時(shí)候首先會(huì)解析APK,而解析APK則需要用到一個(gè)工具類,這個(gè)工具類就是PackageParser

為了讓咱們更好的理解谷歌的安卓團(tuán)隊(duì)對(duì)PackageParser的定位,我們來看下PackageParser的注釋

(一)、PackageParser的注釋

/**
 * Parser for package files (APKs) on disk. This supports apps packaged either
 * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
 * APKs in a single directory.
 * <p>
 * Apps packaged as multiple APKs always consist of a single "base" APK (with a
 * {@code null} split name) and zero or more "split" APKs (with unique split
 * names). Any subset of those split APKs are a valid install, as long as the
 * following constraints are met:
 * <ul>
 * <li>All APKs must have the exact same package name, version code, and signing
 * certificates.
 * <li>All APKs must have unique split names.
 * <li>All installations must contain a single base APK.
 * </ul>
 *
 * @hide
 */
public class PackageParser {
          ...
}

簡單翻譯下:

解析磁盤上的APK安裝包文件。它既能解析一個(gè)"單一"APK文件,也能解析一個(gè)"集群"APK文件(即一個(gè)APK文件里面包含多個(gè)APK文件)。
一個(gè)"集群"APK有一個(gè)"基準(zhǔn)"APK(base APK)組成和其他一些"分割"APK("split" APKs)構(gòu)成,其中這些"分割"APK用一些數(shù)字來分割。這些"分割"APK的必須都是有效的安裝,同時(shí)必須滿足下面的幾個(gè)條件:

  • 所有的APK必須具有完全相同的軟件包名稱,版本代碼和簽名證書
  • 所有的APK必須具有唯一的拆分名稱
  • 所有安裝必須包含一個(gè)單一的APK。

(二)、PackageParser的解析步驟

所以我們知道PackageParse類,它主要用來解析手機(jī)上的APK文件(支持Single APK和MultipleAPK),解析一個(gè)APK主要是分為兩個(gè)步驟:

  • 1、將APK解析成Package:即解析APK文件為Package對(duì)象的過程。
  • 2、將Package轉(zhuǎn)化為PackageInfo:即由Package對(duì)象生成Package對(duì)象生成PackageInfo的過程。

(三)、PackageParser中分類

上面翻譯注釋的時(shí)候,里面提到兩個(gè)概念:Single APKMultiple APK,那我們就來簡單解釋下這兩種APK

Single APK就是我們通常所開發(fā)的APK,即一個(gè)應(yīng)用只有一個(gè)APK文件,而Google Play 還允許你為一個(gè)應(yīng)用中發(fā)布不同的APK文件,這些APK文件適用于不同的設(shè)備,例如:你現(xiàn)在有一個(gè)APP叫DEMO1,但是目前由于APK的體積太大或者其他因素導(dǎo)致不能同時(shí)適用于手機(jī)和平板,此時(shí)你就可以將原先的DEMO.apk,拆分為Demo_phone和Demo_tablet分別用于運(yùn)行在android手機(jī)和Android平板,只要保存兩者擁有相同的包名,并用相同key進(jìn)行簽名就可以在發(fā)布Demo應(yīng)用的時(shí)候,一起發(fā)布Demo_phone和Demo_tablet.apk,那么這種一個(gè)應(yīng)用擁有多個(gè)APK文件的程序就稱為Mutiple APK。

關(guān)于Multiple APK,有想進(jìn)一步研究的可以參考multiple-apks官網(wǎng)

二、PackageParser類的結(jié)構(gòu)

PackageParser類的結(jié)構(gòu)體如下:


image.png

這里面有好多內(nèi)部類和方法。所以我們這里就不挨個(gè)講解了,關(guān)于這個(gè)類的主要方法,后續(xù)在講解APK時(shí)會(huì)用到,所以關(guān)于這個(gè)類,我們先簡單講解下PackageParse類的內(nèi)部類,然后依據(jù)在安裝流程中的安裝入口依次講解PackageParse類中方法

三、PackageParser類的內(nèi)部類簡介

(一) 靜態(tài)內(nèi)部類NewPermissionInfo 類

代碼在PackageParser.java 120行

    /** @hide */
    public static class NewPermissionInfo {
        public final String name;
        public final int sdkVersion;
        public final int fileVersion;
        
        public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
            this.name = name;
            this.sdkVersion = sdkVersion;
            this.fileVersion = fileVersion;
        }
    }

這個(gè)類很簡單,主要是記錄新的權(quán)限:

  • name成員變量:表示權(quán)限的名稱
  • sdkVersion成員變量:表示權(quán)限的開始版本號(hào)
  • fileVersion成員變量:表示文件的版本號(hào),一般為0

(二) 靜態(tài)內(nèi)部類SplitPermissionInfo 類

代碼在PackageParser.java 133行

    /** @hide */
    public static class SplitPermissionInfo {
        public final String rootPerm;
        public final String[] newPerms;
        public final int targetSdk;

        public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) {
            this.rootPerm = rootPerm;
            this.newPerms = newPerms;
            this.targetSdk = targetSdk;
        }
    }

這個(gè)類很簡單,主要是記錄一個(gè)權(quán)限拆分為顆粒度更小的權(quán)限:

  • rootPerm成員變量:表示舊的權(quán)限
  • newPerms成員變量:表示舊的權(quán)限拆分為顆粒度更小的權(quán)限
  • targetSdk成員變量:表示在那個(gè)版本上拆分的

(三) 靜態(tài)內(nèi)部類ParsePackageItemArgs 類

代碼在PackageParser.java 203行

    static class ParsePackageItemArgs {
        final Package owner;
        final String[] outError;
        final int nameRes;
        final int labelRes;
        final int iconRes;
        final int logoRes;
        final int bannerRes;
        
        String tag;
        TypedArray sa;
        
        ParsePackageItemArgs(Package _owner, String[] _outError,
                int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes) {
            owner = _owner;
            outError = _outError;
            nameRes = _nameRes;
            labelRes = _labelRes;
            iconRes = _iconRes;
            logoRes = _logoRes;
            bannerRes = _bannerRes;
        }
    }

這個(gè)類很簡單,主要為解析包單個(gè)item的參數(shù):

  • owner成員變量:表示安裝包的包對(duì)象Package
  • outError成員變量:表示錯(cuò)誤信息
  • nameRes成員變量:表示安裝包中名字對(duì)應(yīng)的資源id
  • labelRes成員變量:表示安裝包中l(wèi)abel對(duì)應(yīng)的資源id
  • iconRes成員變量:表示安裝包中icon對(duì)應(yīng)的資源id
  • logoRes成員變量:表示安裝包中l(wèi)ogo對(duì)應(yīng)的資源id
  • bannerRes成員變量:表示安裝包中banner對(duì)應(yīng)的資源id

(四) 靜態(tài)內(nèi)部類ParseComponentArgs 類

代碼在PackageParser.java 227行

    static class ParseComponentArgs extends ParsePackageItemArgs {
        final String[] sepProcesses;
        final int processRes;
        final int descriptionRes;
        final int enabledRes;
        int flags;
        
        ParseComponentArgs(Package _owner, String[] _outError,
                int _nameRes, int _labelRes, int _iconRes, int _logoRes, int _bannerRes,
                String[] _sepProcesses, int _processRes,
                int _descriptionRes, int _enabledRes) {
            super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes, _bannerRes);
            sepProcesses = _sepProcesses;
            processRes = _processRes;
            descriptionRes = _descriptionRes;
            enabledRes = _enabledRes;
        }
    }

這個(gè)類很簡單,主要為解析包中單個(gè)組件的參數(shù):

  • sepProcesses成員變量:表示該組件對(duì)應(yīng)的進(jìn)程,如果設(shè)置獨(dú)立進(jìn)程則為獨(dú)立進(jìn)程的名字
  • processRes成員變量:表示該組件對(duì)應(yīng)的進(jìn)程的資源id
  • descriptionRes成員變量:表示該組件對(duì)應(yīng)的描述id
  • enabledRes成員變量:表示該組件是否可用
  • flags成員變量:表示該組件的標(biāo)志位

(五) 靜態(tài)內(nèi)部類PackageLite 類

代碼在PackageParser.java 249行

    /**
     * Lightweight parsed details about a single package.
     */
    public static class PackageLite {
        public final String packageName;
        public final int versionCode;
        public final int installLocation;
        public final VerifierInfo[] verifiers;

        /** Names of any split APKs, ordered by parsed splitName */
        public final String[] splitNames;

        /**
         * Path where this package was found on disk. For monolithic packages
         * this is path to single base APK file; for cluster packages this is
         * path to the cluster directory.
         */
        public final String codePath;

        /** Path of base APK */
        public final String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
        public final String[] splitCodePaths;

        /** Revision code of base APK */
        public final int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        public final int[] splitRevisionCodes;

        public final boolean coreApp;
        public final boolean multiArch;
        public final boolean extractNativeLibs;

        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                String[] splitCodePaths, int[] splitRevisionCodes) {
            this.packageName = baseApk.packageName;
            this.versionCode = baseApk.versionCode;
            this.installLocation = baseApk.installLocation;
            this.verifiers = baseApk.verifiers;
            this.splitNames = splitNames;
            this.codePath = codePath;
            this.baseCodePath = baseApk.codePath;
            this.splitCodePaths = splitCodePaths;
            this.baseRevisionCode = baseApk.revisionCode;
            this.splitRevisionCodes = splitRevisionCodes;
            this.coreApp = baseApk.coreApp;
            this.multiArch = baseApk.multiArch;
            this.extractNativeLibs = baseApk.extractNativeLibs;
        }

        public List<String> getAllCodePaths() {
            ArrayList<String> paths = new ArrayList<>();
            paths.add(baseCodePath);
            if (!ArrayUtils.isEmpty(splitCodePaths)) {
                Collections.addAll(paths, splitCodePaths);
            }
            return paths;
        }
    }

通過注釋我們知道這個(gè)類表示在解析過程中的一個(gè)"輕量級(jí)的"、"獨(dú)立的"安裝包

  • packageName成員變量:表示包名
  • versionCode成員變量:表示版本號(hào)
  • installLocation成員變量:安裝位置的屬性,有幾個(gè)常量可以選擇,比如PackageInfo.INSTALL_LOCATION_AUTO的值等。
  • VerifierInfo成員變量:表示驗(yàn)證對(duì)象
  • splitNames成員變量:如果有拆包,則拆包的名字?jǐn)?shù)組,關(guān)于拆包可以參考xxxx
  • codePath成員變量:表示"代碼"的路徑,對(duì)于"單一APK",則對(duì)應(yīng)的是"base APK"的路徑;如果是"集群APK",則對(duì)應(yīng)的是"集群APK"目錄的路徑
  • baseCodePath成員變量:表示"base APK"的路徑
  • splitCodePaths成員變量:表示"拆分"APK的路徑
  • baseRevisionCode成員變量:表示"base APK"的調(diào)整版本號(hào)
  • splitRevisionCodes成員變量:表示"拆分"APK的調(diào)整版本號(hào)
  • coreApp成員變量:表示是不是"核心"APP
  • multiArch成員變量:表示是不是支持多平臺(tái),這里主要是指CPU平臺(tái)
  • extractNativeLibs成員變量:表示是否需要提取"Native庫"(主要是指so)

(六) 靜態(tài)內(nèi)部類ApkLite 類

代碼在PackageParser.java 309行

    /**
     * Lightweight parsed details about a single APK file.
     */
    public static class ApkLite {
        public final String codePath;
        public final String packageName;
        public final String splitName;
        public final int versionCode;
        public final int revisionCode;
        public final int installLocation;
        public final VerifierInfo[] verifiers;
        public final Signature[] signatures;
        public final boolean coreApp;
        public final boolean multiArch;
        public final boolean extractNativeLibs;

        public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                Signature[] signatures, boolean coreApp, boolean multiArch,
                boolean extractNativeLibs) {
            this.codePath = codePath;
            this.packageName = packageName;
            this.splitName = splitName;
            this.versionCode = versionCode;
            this.revisionCode = revisionCode;
            this.installLocation = installLocation;
            this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
            this.signatures = signatures;
            this.coreApp = coreApp;
            this.multiArch = multiArch;
            this.extractNativeLibs = extractNativeLibs;
        }
    }

通過注釋我們知道這個(gè)類表示在解析過程中的一個(gè)"輕量級(jí)的"、"獨(dú)立的"APK

  • codePath成員變量:表示代碼的路徑
  • packageName成員變量:表示包名
  • splitName成員變量:表示"拆包"的包名
  • versionCode成員變量:表示版本號(hào)
  • revisionCode成員變量:表示調(diào)整的版本號(hào)
  • installLocation成員變量:安裝位置的屬性,有幾個(gè)常量可以選擇,比如PackageInfo.INSTALL_LOCATION_AUTO的值等。
  • VerifierInfo成員變量:表示驗(yàn)證對(duì)象
  • Signature成員變量:表示簽名對(duì)象
  • coreApp成員變量:表示是不是"核心"APP
  • multiArch成員變量:表示是不是支持多平臺(tái),這里主要是指CPU平臺(tái)
  • extractNativeLibs成員變量:表示是否需要提取"Native庫"(主要是指so)
PS: PackageLite和ApkLite代表不同的含義,前者是指包,后者是指APK,一個(gè)包中是可能包含多個(gè)APK的。

(七) 私有靜態(tài)內(nèi)部類SplitNameComparator 類

代碼在PackageParser.java 634行

    /**
     * Used to sort a set of APKs based on their split names, always placing the
     * base APK (with {@code null} split name) first.
     */
    private static class SplitNameComparator implements Comparator<String> {
        @Override
        public int compare(String lhs, String rhs) {
            if (lhs == null) {
                return -1;
            } else if (rhs == null) {
                return 1;
            } else {
                return lhs.compareTo(rhs);
            }
        }
    }

這個(gè)類其實(shí)就是一個(gè)比較器,在"拆包"中的排序用的

(八) 靜態(tài)內(nèi)部類Package類

代碼在PackageParser.java 4254行

    /**
     * Representation of a full package parsed from APK files on disk. A package
     * consists of a single base APK, and zero or more split APKs.
     */
    public final static class Package {

        //表示包名
        public String packageName;

        /** Names of any split APKs, ordered by parsed splitName */
        // 表示"拆包"的包名,是個(gè)數(shù)組,每個(gè)元素代表一個(gè)"拆分"包名
        public String[] splitNames;

        // TODO: work towards making these paths invariant
        //對(duì)應(yīng)一個(gè)volume的uid(后面會(huì)有專題去講解volume)
        public String volumeUuid;

        /**
         * Path where this package was found on disk. For monolithic packages
         * this is path to single base APK file; for cluster packages this is
         * path to the cluster directory.
         */
        // 表示代碼的路徑,如果是單個(gè)包,則表示"base"的APK的路徑,如果是"集群"包,則表示的"集群"包的目錄。
        public String codePath;

        /** Path of base APK */
        // "base APK"的路徑
        public String baseCodePath;
        /** Paths of any split APKs, ordered by parsed splitName */
       // "拆分 APK"的路徑
        public String[] splitCodePaths;

        /** Revision code of base APK */
        // "base APK"的調(diào)整版本號(hào)
        public int baseRevisionCode;
        /** Revision codes of any split APKs, ordered by parsed splitName */
        //"拆分APK"的調(diào)整版本號(hào)
        public int[] splitRevisionCodes;

        /** Flags of any split APKs; ordered by parsed splitName */
        // "拆分APK"的標(biāo)志數(shù)組
        public int[] splitFlags;

        /**
         * Private flags of any split APKs; ordered by parsed splitName.
         *
         * {@hide}
         */
        // "拆分APK"的私有標(biāo)志數(shù)組
        public int[] splitPrivateFlags;
  
        // 是否支持硬件加速
        public boolean baseHardwareAccelerated;

        // For now we only support one application per package.
        // 對(duì)應(yīng)ApplicationInfo對(duì)象 對(duì)應(yīng)AndroidManifest里面的Application
        public final ApplicationInfo applicationInfo = new ApplicationInfo();

        // APK安裝包中 AndroidManifest里面的<Permission>
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);

         // APK安裝包中 AndroidManifest里面的<PermissionGroup>
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);

        // APK安裝包中 AndroidManifest里面的<Activity>,這里面的Activity是不是我們通常說的Activity,而是PackageParse的內(nèi)部類Activity
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);

        // APK安裝包中 AndroidManifest里面的<Receiver>,這里面的Activity是不是我們通常說的Activity,而是PackageParse的內(nèi)部類Activity
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);

        // APK安裝包中 AndroidManifest里面的<Provider>,這里面的Provider是不是我們通常說的Provider,而是PackageParse的內(nèi)部類Provider
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);

        // APK安裝包中 AndroidManifest里面的<Service>,這里面的Service是不是我們通常說的Service,而是PackageParse的內(nèi)部類Service
        public final ArrayList<Service> services = new ArrayList<Service>(0);

        // APK安裝包中 AndroidManifest里面的<Instrumentation>,這里面的Instrumentation是不是我們通常說的Instrumentation,而是PackageParse的內(nèi)部類Instrumentation
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);

        // APK安裝包中請(qǐng)求的權(quán)限
        public final ArrayList<String> requestedPermissions = new ArrayList<String>();

        // APK安裝包中 保內(nèi)廣播的Action
        public ArrayList<String> protectedBroadcasts;

        // APK安裝包中 依賴庫的名字
        public ArrayList<String> libraryNames = null;

        // APK安裝包中 使用庫的名字
        public ArrayList<String> usesLibraries = null;

        // APK安裝包中 使用選項(xiàng)庫的名字
        public ArrayList<String> usesOptionalLibraries = null;

        // APK安裝包中 使用庫的路徑數(shù)組
        public String[] usesLibraryFiles = null;

        // APK安裝包中 某個(gè)Activity信息的集合,在AndroidManifest里面的<preferred>標(biāo)簽(不在receiver里面)
        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;

        // APK安裝包中 AndroidManifest中對(duì)應(yīng)"original-package"的集合
        public ArrayList<String> mOriginalPackages = null;

        // 是真實(shí)包名,通常和mOriginalPackages一起使用
        public String mRealPackage = null;

         // APK安裝包中 AndroidManifest中對(duì)應(yīng)"adopt-permissions"集合
        public ArrayList<String> mAdoptPermissions = null;
        
        // We store the application meta-data independently to avoid multiple unwanted references
         // 我們獨(dú)立的存儲(chǔ)應(yīng)用程序元數(shù)據(jù),以避免多個(gè)不需要的引用
        public Bundle mAppMetaData = null;

        // The version code declared for this package.
        //  版本號(hào)
        public int mVersionCode;

        // The version name declared for this package.
        // 版本名
        public String mVersionName;
        
        // The shared user id that this package wants to use.
         // 共享id
        public String mSharedUserId;

        // The shared user label that this package wants to use.
        // 共享用戶標(biāo)簽
        public int mSharedUserLabel;

        // Signatures that were read from the package.
         // 簽名
        public Signature[] mSignatures;
        // 證書
        public Certificate[][] mCertificates;

        // For use by package manager service for quick lookup of
        // preferred up order. 
        // dexopt的位置,以便PackageManagerService跟蹤要執(zhí)行dexopt的位置
        public int mPreferredOrder = 0;

        // For use by package manager to keep track of where it needs to do dexopt.
         //  需要進(jìn)行的dexopt的集合
        public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4);

        // For use by package manager to keep track of when a package was last used.
        // 最后一次使用pakcage的時(shí)間的
        public long mLastPackageUsageTimeInMills;

        // // User set enabled state.
        // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
        //
        // // Whether the package has been stopped.
        // public boolean mSetStopped = false;

        // Additional data supplied by callers.
        // 附加數(shù)據(jù)
        public Object mExtras;

        // Applications hardware preferences
         // 硬件配置信息,對(duì)一個(gè)AndroidManifest里面的<uses-configuration> 標(biāo)簽
        public ArrayList<ConfigurationInfo> configPreferences = null;

        // Applications requested features
         //特性信息,對(duì)一個(gè)AndroidManifest里面的<uses-feature> 標(biāo)簽 
        public ArrayList<FeatureInfo> reqFeatures = null;

        // Applications requested feature groups
         //特性組信息,對(duì)一個(gè)AndroidManifest里面的<feature-group> 標(biāo)簽 
        public ArrayList<FeatureGroupInfo> featureGroups = null;

         // 安裝的屬性
        public int installLocation;

        // 是否是核心
        public boolean coreApp;

        /* An app that's required for all users and cannot be uninstalled for a user */
        // 是否是全局必要,所有用戶都需要的應(yīng)用程序,無法為用戶卸載
        public boolean mRequiredForAllUsers;

        /* The restricted account authenticator type that is used by this application */
        // 受限賬戶的 驗(yàn)證類型
        public String mRestrictedAccountType;

        /* The required account type without which this application will not function */
          //  賬戶的類型
        public String mRequiredAccountType;

        /**
         * Digest suitable for comparing whether this package's manifest is the
         * same as another.
         */
        // 對(duì)應(yīng)的AndroidManifest項(xiàng)目摘要清單
        public ManifestDigest manifestDigest;

        //AndroidManifest中 對(duì)應(yīng)<overlay> 標(biāo)簽
        public String mOverlayTarget;
     
        //overlay對(duì)應(yīng)的優(yōu)先級(jí)
        public int mOverlayPriority;

         //  是否是受信任的Overlay
        public boolean mTrustedOverlay;

        /**
         * Data used to feed the KeySetManagerService
         */
         // 下面是用來給KeySetManagerService的數(shù)據(jù)
        public ArraySet<PublicKey> mSigningKeys;  // 簽名
        public ArraySet<String> mUpgradeKeySets;  //升級(jí)
        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;  //公鑰

        /**
         * The install time abi override for this package, if any.
         *
         * TODO: This seems like a horrible place to put the abiOverride because
         * this isn't something the packageParser parsers. However, this fits in with
         * the rest of the PackageManager where package scanning randomly pushes
         * and prods fields out of {@code this.applicationInfo}.
         */
        // 如果有abi的話,abi覆蓋
        public String cpuAbiOverride;

        public Package(String packageName) {
            this.packageName = packageName;
            applicationInfo.packageName = packageName;
            applicationInfo.uid = -1;
        }
         ...
    }

先來翻譯一下注釋:

這個(gè)類表示從磁盤上的APK文件解析出來的完整包。一個(gè)包由一個(gè)"基礎(chǔ)"APK和多個(gè)"拆分"APK構(gòu)成。

這個(gè)類其實(shí)就是通過解析APK而對(duì)應(yīng)的一個(gè)"包"的類,這個(gè)包代表一個(gè)磁盤上的APK安裝包。

關(guān)于每個(gè)字段的含義,我在每個(gè)字段上都添加了注釋,關(guān)于這個(gè)類的一些方法,我會(huì)在后面講解方法的時(shí)候講解

(九) 靜態(tài)內(nèi)部類Component類

代碼在PackageParser.java 5044行

    public static class IntentInfo extends IntentFilter {
        public boolean hasDefault; //是否有默認(rèn)
        public int labelRes;  // 標(biāo)簽的資源id
        public CharSequence nonLocalizedLabel; // 本地化的標(biāo)簽
        public int icon; // icon的資源id
        public int logo; // logo的資源id
        public int banner; // banner的資源id
        public int preferred; // preferred的資源id
    }

IntentInfo類繼承自IntentFilter,就是對(duì)IntentFilter進(jìn)行了封裝。

(十) 靜態(tài)內(nèi)部類Component類

代碼在PackageParser.java 4542行

    public static class Component<II extends IntentInfo> {
        public final Package owner;  //包含該組件的包
        public final ArrayList<II> intents; // 該組件所包含的IntentFilter
        public final String className; // 組件的類名
        public Bundle metaData;   // 組件的元數(shù)據(jù)

        ComponentName componentName;  //組件名
        String componentShortName; //組件簡稱

        public Component(Package _owner) {
            owner = _owner;
            intents = null;
            className = null;
        }

        public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) {
            owner = args.owner;
            intents = new ArrayList<II>(0);
            if (parsePackageItemInfo(args.owner, outInfo, args.outError, args.tag, args.sa,
                    true /*nameRequired*/, args.nameRes, args.labelRes, args.iconRes,
                    args.roundIconRes, args.logoRes, args.bannerRes)) {
                className = outInfo.name;
            } else {
                className = null;
            }
        }

        public Component(final ParseComponentArgs args, final ComponentInfo outInfo) {
            this(args, (PackageItemInfo)outInfo);
            if (args.outError[0] != null) {
                return;
            }

            if (args.processRes != 0) {
                CharSequence pname;
                if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
                    pname = args.sa.getNonConfigurationString(args.processRes,
                            Configuration.NATIVE_CONFIG_VERSION);
                } else {
                    // Some older apps have been seen to use a resource reference
                    // here that on older builds was ignored (with a warning).  We
                    // need to continue to do this for them so they don't break.
                    pname = args.sa.getNonResourceString(args.processRes);
                }
                outInfo.processName = buildProcessName(owner.applicationInfo.packageName,
                        owner.applicationInfo.processName, pname,
                        args.flags, args.sepProcesses, args.outError);
            }

            if (args.descriptionRes != 0) {
                outInfo.descriptionRes = args.sa.getResourceId(args.descriptionRes, 0);
            }

            outInfo.enabled = args.sa.getBoolean(args.enabledRes, true);
        }

        public Component(Component<II> clone) {
            owner = clone.owner;
            intents = clone.intents;
            className = clone.className;
            componentName = clone.componentName;
            componentShortName = clone.componentShortName;
        }
       ...
    }

組件類是個(gè)基類。里面包含組件對(duì)應(yīng)的各個(gè)屬性。

(十一) 靜態(tài)內(nèi)部類Permission類

代碼在PackageParser.java 4663行

    public final static class Permission extends Component<IntentInfo> {
        public final PermissionInfo info;  //權(quán)限信息
        public boolean tree;  //是否權(quán)限樹
        public PermissionGroup group;  //對(duì)應(yīng)的權(quán)限組

        public Permission(Package _owner) {
            super(_owner);
            info = new PermissionInfo();
        }

        public Permission(Package _owner, PermissionInfo _info) {
            super(_owner);
            info = _info;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            return "Permission{"
                + Integer.toHexString(System.identityHashCode(this))
                + " " + info.name + "}";
        }
    }

該類繼承自Component,對(duì)應(yīng)AndroidManifest里面的<Permission>標(biāo)簽,含義為標(biāo)簽

(十二) 靜態(tài)內(nèi)部類PermissionGroup類

代碼在PackageParser.java 4690行

    public final static class PermissionGroup extends Component<IntentInfo> {
        public final PermissionGroupInfo info;  //權(quán)限組對(duì)象

        public PermissionGroup(Package _owner) {
            super(_owner);
            info = new PermissionGroupInfo();
        }

        public PermissionGroup(Package _owner, PermissionGroupInfo _info) {
            super(_owner);
            info = _info;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            return "PermissionGroup{"
                + Integer.toHexString(System.identityHashCode(this))
                + " " + info.name + "}";
        }
    }

這個(gè)PermissionGroup繼承自Component,意為權(quán)限組,對(duì)應(yīng)AndroidManifest里面的<PermissionGroup>

(十三) 靜態(tài)內(nèi)部類Activity類

代碼在PackageParser.java 4860行

    public final static class Activity extends Component<ActivityIntentInfo> {
        public final ActivityInfo info;

        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
            super(args, _info);
            info = _info;
            info.applicationInfo = args.owner.applicationInfo;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Activity{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)Activity繼承自Component,對(duì)應(yīng)AndroidManifest里面的<Activity>,其實(shí)就是ActivityInfo的封裝類。

(十四) 靜態(tài)內(nèi)部類Service類

代碼在PackageParser.java 4914行

   public final static class Service extends Component<ServiceIntentInfo> {
        public final ServiceInfo info;

        public Service(final ParseComponentArgs args, final ServiceInfo _info) {
            super(args, _info);
            info = _info;
            info.applicationInfo = args.owner.applicationInfo;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Service{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)Service繼承自Component,對(duì)應(yīng)AndroidManifest里面的<Service>,其實(shí)就是ServiceInfo的封裝類。

(十五) 靜態(tài)內(nèi)部類Provider類

代碼在PackageParser.java 4955行

    public final static class Provider extends Component<ProviderIntentInfo> {
        public final ProviderInfo info;
        public boolean syncable;

        public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
            super(args, _info);
            info = _info;
            info.applicationInfo = args.owner.applicationInfo;
            syncable = false;
        }

        public Provider(Provider existingProvider) {
            super(existingProvider);
            this.info = existingProvider.info;
            this.syncable = existingProvider.syncable;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Provider{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)Provider繼承自Component,對(duì)應(yīng)AndroidManifest里面的<Provider>,其實(shí)就是ProviderInfo的封裝類。

(十六) 靜態(tài)內(nèi)部類Instrumentation類

代碼在PackageParser.java 5009行

    public final static class Instrumentation extends Component<IntentInfo> {
        public final InstrumentationInfo info;

        public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
            super(args, _info);
            info = _info;
        }

        public void setPackageName(String packageName) {
            super.setPackageName(packageName);
            info.packageName = packageName;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("Instrumentation{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)Instrumentation繼承自Component,對(duì)應(yīng)AndroidManifest里面的<Instrumentation>,其實(shí)就是InstrumentationInfo的封裝類。

(十七) 靜態(tài)內(nèi)部類ActivityIntentInfo類

代碼在PackageParser.java 5054行

    public final static class ActivityIntentInfo extends IntentInfo {
        public final Activity activity;

        public ActivityIntentInfo(Activity _activity) {
            activity = _activity;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("ActivityIntentInfo{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            activity.appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)ActivityIntentInfo繼承自IntentInfo,其實(shí)就是Activity的封裝類。

(十八) 靜態(tài)內(nèi)部類ServiceIntentInfo類

代碼在PackageParser.java 5054行

    public final static class ServiceIntentInfo extends IntentInfo {
        public final Service service;

        public ServiceIntentInfo(Service _service) {
            service = _service;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("ServiceIntentInfo{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            service.appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)ServiceIntentInfo繼承自IntentInfo,其實(shí)就是Service的封裝類。

(十九) 靜態(tài)內(nèi)部類ProviderIntentInfo類

代碼在PackageParser.java 5090行

    public static final class ProviderIntentInfo extends IntentInfo {
        public final Provider provider;

        public ProviderIntentInfo(Provider provider) {
            this.provider = provider;
        }

        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("ProviderIntentInfo{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(' ');
            provider.appendComponentShortName(sb);
            sb.append('}');
            return sb.toString();
        }
    }

這個(gè)ProviderIntentInfo繼承自IntentInfo,其實(shí)就是provider的封裝類。

(二十) 靜態(tài)內(nèi)部類PackageParserException類

代碼在PackageParser.java 5142行

    public static class PackageParserException extends Exception {
        public final int error;

        public PackageParserException(int error, String detailMessage) {
            super(detailMessage);
            this.error = error;
        }

        public PackageParserException(int error, String detailMessage, Throwable throwable) {
            super(detailMessage, throwable);
            this.error = error;
        }
    }

這個(gè)PackageParserException繼承自Exception,其實(shí)就是Exception的封裝類。

至此整個(gè)PackageParser的內(nèi)部類全部簡介完畢,下面我們來看下解析的方法

四、PackageParse#parsePackage(File, int)方法解析

代碼在PackageParser.java 752行

    /**
     * Parse the package at the given location. Automatically detects if the
     * package is a monolithic style (single APK file) or cluster style
     * (directory of APKs).
     * <p>
     * This performs sanity checking on cluster style packages, such as
     * requiring identical package name and version codes, a single base APK,
     * and unique split names.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link #collectCertificates(Package, int)}.
     *
     * @see #parsePackageLite(File, int)
     */
    public Package parsePackage(File packageFile, int flags) throws PackageParserException {
        if (packageFile.isDirectory()) {
            return parseClusterPackage(packageFile, flags);
        } else {
            return parseMonolithicPackage(packageFile, flags);
        }
    }

這個(gè)方法有注釋,先來看下注釋:

解析指定位置的安裝包。它自動(dòng)會(huì)檢測(cè)安裝包的模式的是單一APK或者集群APK模式。
這樣就可以對(duì)"集群APK"的安裝包進(jìn)行理性的檢查,比如會(huì)檢查"base APK"和"拆分APK"是否具有相同的包名和版本號(hào)。
請(qǐng)注意,這里面是不執(zhí)行簽名驗(yàn)證的,所以必須要單獨(dú)執(zhí)行collectCertificates(Package,int)這個(gè)方法

這個(gè)方法很簡單,主要是判斷是否是目錄,如果是目錄則調(diào)用parseMonolithicPackage(File,int)方法,如果不是目錄,是文件則調(diào)用parseClusterPackage(File,int)

五、PackageParse#parseMonolithicPackage(File, int)方法解析

代碼在PackageParser.java 827行

    /**
     * Parse the given APK file, treating it as as a single monolithic package.
     * <p>
     * Note that this <em>does not</em> perform signature verification; that
     * must be done separately in {@link #collectCertificates(Package, int)}.
     *
     * @deprecated external callers should move to
     *             {@link #parsePackage(File, int)}. Eventually this method will
     *             be marked private.
     */
    @Deprecated
    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 {
            final Package pkg = parseBaseApk(apkFile, assets, flags);
            pkg.codePath = apkFile.getAbsolutePath();
            return pkg;
        } finally {
            IoUtils.closeQuietly(assets);
        }
    }

先簡單翻譯一下注釋:

解析給定的APK文件,將其視為單一APK包
注意:這個(gè)不執(zhí)行簽名,必須在collectCertificates方法中獨(dú)立完成。

我們知道這個(gè)方法內(nèi)部主要是:

  • 首先 判斷是不是mOnlyCoreApps,mOnlyCoreApps該標(biāo)示表明解析只考慮應(yīng)用清單屬性有效的應(yīng)用,主要為了創(chuàng)建一個(gè)最小的啟動(dòng)環(huán)境,如果該標(biāo)示為true則表示為輕量級(jí)解析,調(diào)用parseMonolithicPackageLite來進(jìn)行解析
  • 其次 如果mOnlyCoreApps不為空,則new了一個(gè)AssetManager對(duì)象
  • 再次 調(diào)用parseBaseApk()方法解析一個(gè)apk并生成一個(gè)Package對(duì)象
  • 最后 給pkg的codePath賦值

這里面涉及了兩個(gè)方法分別是parseMonolithicPackageLite(apkFile, flags);parseBaseApk(),下面我們就詳細(xì)看下

六、PackageParse#parseMonolithicPackageLite(File, int)方法解析

代碼在PackageParser.java 657行

    private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
            throws PackageParserException {
        final ApkLite baseApk = parseApkLite(packageFile, flags);
        final String packagePath = packageFile.getAbsolutePath();
        return new PackageLite(packagePath, baseApk, null, null, null);
    }

這個(gè)方法很賤,就是調(diào)用parseApk()方法來獲取一個(gè)ApkLite對(duì)象,然后用這個(gè)ApkLite對(duì)象構(gòu)造一個(gè)PackageLite對(duì)象

所以正在這個(gè)函數(shù)里面要解決三個(gè)問題

  • 1、parseApkLite(File,int)函數(shù)內(nèi)部的實(shí)現(xiàn)
  • 2、ApkLite類
  • 3、PackageLite類

parseMonolithicPackageLite內(nèi)部又調(diào)用了parseApkLite函數(shù)并且返回一個(gè)ApkLite對(duì)象,根據(jù)返回的ApkLite對(duì)象和包的絕對(duì)路徑構(gòu)造了一個(gè)PackegeLite對(duì)象作為返回值。

下面我們來看下parseApkLite(File,int)方法

七、PackageParse#parseApkLite(File,int)方法解析

代碼在PackageParser.java 1155行

    /**
     * Utility method that retrieves lightweight details about a single APK
     * file, including package name, split name, and install location.
     *
     * @param apkFile path to a single APK
     * @param flags optional parse flags, such as
     *            {@link #PARSE_COLLECT_CERTIFICATES}
     */
    public static ApkLite parseApkLite(File apkFile, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        AssetManager assets = null;
        XmlResourceParser parser = null;
        try {
            assets = new AssetManager();
            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);

            int cookie = assets.addAssetPath(apkPath);
            if (cookie == 0) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                        "Failed to parse " + apkPath);
            }

            final DisplayMetrics metrics = new DisplayMetrics();
            metrics.setToDefaults();

            final Resources res = new Resources(assets, metrics, null);
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final Signature[] signatures;
            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                // TODO: factor signature related items out of Package object
                final Package tempPkg = new Package(null);
                collectCertificates(tempPkg, apkFile, 0);
                signatures = tempPkg.mSignatures;
            } else {
                signatures = null;
            }

            final AttributeSet attrs = parser;
            return parseApkLite(apkPath, res, parser, attrs, flags, signatures);

        } catch (XmlPullParserException | IOException | RuntimeException e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to parse " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
            IoUtils.closeQuietly(assets);
        }
    }

這個(gè)方法內(nèi)部的流程如下:

  • 1、創(chuàng)建AssetManager對(duì)象assets
  • 2、assets調(diào)用addAssetPath(apkPath)
  • 3、用assets作為入?yún)韯?chuàng)建Resources對(duì)象res
  • 4、調(diào)用assets的openXmlResourceParser()來獲取XmlResourceParser對(duì)象parser對(duì)象
  • 5、設(shè)置簽名
  • 6、調(diào)用parseApkLite(String,Resource,XmlPullParser,AttributeSet,int,Signature[])方法來返回一個(gè)ApkLite對(duì)象

總結(jié)一下就是:

函數(shù)里面初始化了一個(gè)AssertMananger對(duì)象,一個(gè)DisplayMetrics對(duì)象,一個(gè)Resources對(duì)象,并調(diào)用collectCertificates函數(shù)獲取了應(yīng)用的簽名信息,這些對(duì)象都是后續(xù)解析中需要用的,因此將這些函數(shù)傳遞給解析函數(shù),這些對(duì)象都是后續(xù)解析中需要用的,因此將這些參數(shù)傳遞給解析函數(shù),解析完成后關(guān)閉資源管理器與解析器,這里主要是輕量級(jí)解析,只解析了包名,安裝位置等少量信息。

下面我們來看看解析過程:

八、PackageParse#parseApkLite(File,int)方法解析

代碼在PackageParser.java 1274行

    private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
            AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
            XmlPullParserException, PackageParserException {
        final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);

        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
        int versionCode = 0;
        int revisionCode = 0;
        boolean coreApp = false;
        boolean multiArch = false;
        boolean extractNativeLibs = true;

        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            final String attr = attrs.getAttributeName(i);
            if (attr.equals("installLocation")) {
                installLocation = attrs.getAttributeIntValue(i,
                        PARSE_DEFAULT_INSTALL_LOCATION);
            } else if (attr.equals("versionCode")) {
                versionCode = attrs.getAttributeIntValue(i, 0);
            } else if (attr.equals("revisionCode")) {
                revisionCode = attrs.getAttributeIntValue(i, 0);
            } else if (attr.equals("coreApp")) {
                coreApp = attrs.getAttributeBooleanValue(i, false);
            }
        }

        // Only search the tree when the tag is directly below <manifest>
        int type;
        final int searchDepth = parser.getDepth() + 1;

        final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags);
                if (verifier != null) {
                    verifiers.add(verifier);
                }
            }

            if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) {
                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                    final String attr = attrs.getAttributeName(i);
                    if ("multiArch".equals(attr)) {
                        multiArch = attrs.getAttributeBooleanValue(i, false);
                    }
                    if ("extractNativeLibs".equals(attr)) {
                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                    }
                }
            }
        }

        return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
                revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
                extractNativeLibs);
    }
  • 首先 這個(gè)方法又調(diào)用了parsePackageSplitNames()方法來獲取Pair<String, String> packageSplit
  • 其次 遍歷屬性,并獲取相應(yīng)的值,這里面主要是循環(huán)解析出installLocation,versionCode,revisionCode,coreApp
  • 再次 繼續(xù)解析package-verifier節(jié)點(diǎn),該節(jié)點(diǎn)主要解析兩個(gè)屬性:
    • multiArch
    • extractNativeLibs
  • 最后 利用解析出來的數(shù)據(jù)去構(gòu)造一個(gè)ApkLite對(duì)象

九、PackageParse#parsePackageSplitNames(XmlpullParse,AttributeSet,int)方法解析

代碼在PackageParser.java 1230行

    private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
            AttributeSet attrs, int flags) throws IOException, XmlPullParserException,
            PackageParserException {

        int type;
        while ((type = parser.next()) != XmlPullParser.START_TAG
                && type != XmlPullParser.END_DOCUMENT) {
        }

        if (type != XmlPullParser.START_TAG) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "No start tag found");
        }
        if (!parser.getName().equals("manifest")) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "No <manifest> tag");
        }

        final String packageName = attrs.getAttributeValue(null, "package");
        if (!"android".equals(packageName)) {
            final String error = validateName(packageName, true, true);
            if (error != null) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                        "Invalid manifest package: " + error);
            }
        }

        String splitName = attrs.getAttributeValue(null, "split");
        if (splitName != null) {
            if (splitName.length() == 0) {
                splitName = null;
            } else {
                final String error = validateName(splitName, false, false);
                if (error != null) {
                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
                            "Invalid manifest split: " + error);
                }
            }
        }

        return Pair.create(packageName.intern(),
                (splitName != null) ? splitName.intern() : splitName);
    }

這個(gè)方法主要是解析manifest,這里面調(diào)用了validateName(String, boolean,boolean)方法,這里簡單說下,這個(gè)方法主要就是檢測(cè)是否是數(shù)字、字母、下劃線和點(diǎn)分隔符,這也是取包名的規(guī)則,比如是字母數(shù)字下劃線加點(diǎn)分隔符,否則都不是合法的應(yīng)用包名。并且合法的包名至少包含一個(gè)點(diǎn)分隔符。

image.png

十、PackageParse#parseBaseApk(File,AssetManager,int)方法解析

代碼在PackageParser.java 864行

    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        String volumeUuid = null;
        if (apkPath.startsWith(MNT_EXPAND)) {
            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
        }

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = apkFile.getAbsolutePath();

        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);

        Resources res = null;
        XmlResourceParser parser = null;
        try {
            res = new Resources(assets, mMetrics, null);
            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    Build.VERSION.RESOURCES_SDK_INT);
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final String[] outError = new String[1];
            final Package pkg = parseBaseApk(res, parser, flags, outError);
            if (pkg == null) {
                throw new PackageParserException(mParseError,
                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
            }

            pkg.volumeUuid = volumeUuid;
            pkg.applicationInfo.volumeUuid = volumeUuid;
            pkg.baseCodePath = apkPath;
            pkg.mSignatures = null;

            return pkg;

        } catch (PackageParserException e) {
            throw e;
        } catch (Exception e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
        }
    }

這個(gè)方法內(nèi)部主要就是做了兩件事

  • 1、解析volumeUuid
  • 2、調(diào)用parseBaseApk(res, parser, flags, outError)來獲取Package對(duì)象pkg并返回

簡單的來說:

這個(gè)方法和前面幾步的輕量級(jí)解析一致,主要多了一個(gè)步驟解析volumeUuid,如果APK路徑的前置為"/mnt/expand/",則獲取從前綴之后的uuid,從而可以根據(jù)這個(gè)路徑獲取文件的路徑。

下面我們來看下parseBaseApk(res, parser, flags, outError)的具體實(shí)現(xiàn):

十一、PackageParse#parseBaseApk(Resources,XmlResourceParser,int,String[])方法解析

代碼在PackageParser.java 1354行

    /**
     * Parse the manifest of a <em>base APK</em>.
     * <p>
     * When adding new features, carefully consider if they should also be
     * supported by split APKs.
     */
    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
        
        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;

        AttributeSet attrs = parser;

        mParseInstrumentationArgs = null;
        mParseActivityArgs = null;
        mParseServiceArgs = null;
        mParseProviderArgs = null;

        // 因?yàn)槭墙馕?base" APK,所以應(yīng)該出現(xiàn)"拆包"APK的信息,如果出現(xiàn),則返回
        final String pkgName;
        final String splitName;
        try {
            //調(diào)用parsePackageSplitNames獲取packageSplit
            Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
            pkgName = packageSplit.first;
            splitName = packageSplit.second;
        } catch (PackageParserException e) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }

        int type;

        if (!TextUtils.isEmpty(splitName)) {
            outError[0] = "Expected base APK, but found split " + splitName;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }

        // 用包名構(gòu)造一個(gè)Package
        final Package pkg = new Package(pkgName);
        boolean foundApp = false;

        // 獲取資源數(shù)組
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifest);

        // 初始化pkg的屬性mVersionCode、baseRevisionCode和mVersionName
        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        pkg.baseRevisionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
        pkg.mVersionName = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_versionName, 0);
        if (pkg.mVersionName != null) {
            pkg.mVersionName = pkg.mVersionName.intern();
        }

        // 獲取sharedUID
        String str = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);

         // 如果sharedUID不為空
        if (str != null && str.length() > 0) {
            String nameError = validateName(str, true, false);
            // 如果不是系統(tǒng)包,即framework-res.apk(它的包名為"android"),則報(bào)錯(cuò)
            if (nameError != null && !"android".equals(pkgName)) {
                outError[0] = "<manifest> specifies bad sharedUserId name \""
                    + str + "\": " + nameError;
                mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
                return null;
            }
            pkg.mSharedUserId = str.intern();
            pkg.mSharedUserLabel = sa.getResourceId(
                    com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
        }

        // 獲取 安裝路徑的設(shè)置
        pkg.installLocation = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_installLocation,
                PARSE_DEFAULT_INSTALL_LOCATION);
        pkg.applicationInfo.installLocation = pkg.installLocation;

        // 是不是核心app
        pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false);

        sa.recycle();

        /* Set the global "forward lock" flag */
        if ((flags & PARSE_FORWARD_LOCK) != 0) {
            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
        }

        // 是否要安裝在SD卡上
        /* Set the global "on SD card" flag */
        if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
        }

        // Resource boolean are -1, so 1 means we don't know the value.
        int supportsSmallScreens = 1;
        int supportsNormalScreens = 1;
        int supportsLargeScreens = 1;
        int supportsXLargeScreens = 1;
        int resizeable = 1;
        int anyDensity = 1;

        int outerDepth = parser.getDepth();
    
        // 開始解析xml
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            // 解析<application> 標(biāo)簽
            if (tagName.equals("application")) {
                if (foundApp) {
                    if (RIGID_PARSER) {
                        outError[0] = "<manifest> has more than one <application>";
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                        return null;
                    } else {
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                }

                foundApp = true;
                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
                    return null;
                }
            } else if (tagName.equals("overlay")) {
                  // 解析<overlay> 標(biāo)簽
                pkg.mTrustedOverlay = trustedOverlay;

                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                pkg.mOverlayTarget = sa.getString(
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
                pkg.mOverlayPriority = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
                        -1);
                sa.recycle();

                if (pkg.mOverlayTarget == null) {
                    outError[0] = "<overlay> does not specify a target package";
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }
                if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
                    outError[0] = "<overlay> priority must be between 0 and 9999";
                    mParseError =
                        PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return null;
                }
                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("key-sets")) {
                 // 解析<key-sets> 標(biāo)簽
                if (!parseKeySets(pkg, res, parser, attrs, outError)) {
                    return null;
                }
            } else if (tagName.equals("permission-group")) {
                // 解析<permission-group> 標(biāo)簽
                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission")) {
                // 解析<permission> 標(biāo)簽
                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission-tree")) {
                // 解析<permission-tree> 標(biāo)簽
                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("uses-permission")) {
                 // 解析<uses-permission> 標(biāo)簽
                if (!parseUsesPermission(pkg, res, parser, attrs)) {
                    return null;
                }
            } else if (tagName.equals("uses-permission-sdk-m")
                    || tagName.equals("uses-permission-sdk-23")) {
                // 解析<uses-permission-sdk-m> 標(biāo)簽 或者 <uses-permission-sdk-23> 標(biāo)簽
                if (!parseUsesPermission(pkg, res, parser, attrs)) {
                    return null;
                }
            } else if (tagName.equals("uses-configuration")) {
                // 解析<uses-configuration>  標(biāo)簽
                ConfigurationInfo cPref = new ConfigurationInfo();
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
                cPref.reqTouchScreen = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
                        Configuration.TOUCHSCREEN_UNDEFINED);
                cPref.reqKeyboardType = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
                        Configuration.KEYBOARD_UNDEFINED);
                if (sa.getBoolean(
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
                        false)) {
                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
                }
                cPref.reqNavigation = sa.getInt(
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
                        Configuration.NAVIGATION_UNDEFINED);
                if (sa.getBoolean(
                        com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
                        false)) {
                    cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
                }
                sa.recycle();
                pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("uses-feature")) {
                  // 解析<uses-feature>  標(biāo)簽
                FeatureInfo fi = parseUsesFeature(res, attrs);
                pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);

                if (fi.name == null) {
                    ConfigurationInfo cPref = new ConfigurationInfo();
                    cPref.reqGlEsVersion = fi.reqGlEsVersion;
                    pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("feature-group")) {
                 //解析  <feature-group> 標(biāo)簽
                FeatureGroupInfo group = new FeatureGroupInfo();
                ArrayList<FeatureInfo> features = null;
                final int innerDepth = parser.getDepth();
                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                        continue;
                    }

                    final String innerTagName = parser.getName();
                    if (innerTagName.equals("uses-feature")) {
                        FeatureInfo featureInfo = parseUsesFeature(res, attrs);
                        // FeatureGroups are stricter and mandate that
                        // any <uses-feature> declared are mandatory.
                        featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
                        features = ArrayUtils.add(features, featureInfo);
                    } else {
                        Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
                                " at " + mArchiveSourcePath + " " +
                                parser.getPositionDescription());
                    }
                    XmlUtils.skipCurrentTag(parser);
                }

                if (features != null) {
                    group.features = new FeatureInfo[features.size()];
                    group.features = features.toArray(group.features);
                }
                pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);

            } else if (tagName.equals("uses-sdk")) {
                 // 解析 <uses-sdk> 標(biāo)簽
                if (SDK_VERSION > 0) {
                    sa = res.obtainAttributes(attrs,
                            com.android.internal.R.styleable.AndroidManifestUsesSdk);

                    int minVers = 0;
                    String minCode = null;
                    int targetVers = 0;
                    String targetCode = null;

                    TypedValue val = sa.peekValue(
                            com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
                    if (val != null) {
                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                            targetCode = minCode = val.string.toString();
                        } else {
                            // If it's not a string, it's an integer.
                            targetVers = minVers = val.data;
                        }
                    }

                    val = sa.peekValue(
                            com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
                    if (val != null) {
                        if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                            targetCode = minCode = val.string.toString();
                        } else {
                            // If it's not a string, it's an integer.
                            targetVers = val.data;
                        }
                    }

                    sa.recycle();

                    if (minCode != null) {
                        boolean allowedCodename = false;
                        for (String codename : SDK_CODENAMES) {
                            if (minCode.equals(codename)) {
                                allowedCodename = true;
                                break;
                            }
                        }
                        if (!allowedCodename) {
                            if (SDK_CODENAMES.length > 0) {
                                outError[0] = "Requires development platform " + minCode
                                        + " (current platform is any of "
                                        + Arrays.toString(SDK_CODENAMES) + ")";
                            } else {
                                outError[0] = "Requires development platform " + minCode
                                        + " but this is a release platform.";
                            }
                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                            return null;
                        }
                    } else if (minVers > SDK_VERSION) {
                        outError[0] = "Requires newer sdk version #" + minVers
                                + " (current version is #" + SDK_VERSION + ")";
                        mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                        return null;
                    }

                    if (targetCode != null) {
                        boolean allowedCodename = false;
                        for (String codename : SDK_CODENAMES) {
                            if (targetCode.equals(codename)) {
                                allowedCodename = true;
                                break;
                            }
                        }
                        if (!allowedCodename) {
                            if (SDK_CODENAMES.length > 0) {
                                outError[0] = "Requires development platform " + targetCode
                                        + " (current platform is any of "
                                        + Arrays.toString(SDK_CODENAMES) + ")";
                            } else {
                                outError[0] = "Requires development platform " + targetCode
                                        + " but this is a release platform.";
                            }
                            mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                            return null;
                        }
                        // If the code matches, it definitely targets this SDK.
                        pkg.applicationInfo.targetSdkVersion
                                = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
                    } else {
                        pkg.applicationInfo.targetSdkVersion = targetVers;
                    }
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("supports-screens")) {
                 // 解析 <supports-screens> 標(biāo)簽
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens);

                pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
                        0);
                pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
                        0);
                pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
                        0);

                // This is a trick to get a boolean and still able to detect
                // if a value was actually set.
                supportsSmallScreens = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
                        supportsSmallScreens);
                supportsNormalScreens = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
                        supportsNormalScreens);
                supportsLargeScreens = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
                        supportsLargeScreens);
                supportsXLargeScreens = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
                        supportsXLargeScreens);
                resizeable = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
                        resizeable);
                anyDensity = sa.getInteger(
                        com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
                        anyDensity);

                sa.recycle();

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("protected-broadcast")) {
                // 解析<protected-broadcast> 標(biāo)簽
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

                // Note: don't allow this value to be a reference to a resource
                // that may change.
                String name = sa.getNonResourceString(
                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

                sa.recycle();

                if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
                    if (pkg.protectedBroadcasts == null) {
                        pkg.protectedBroadcasts = new ArrayList<String>();
                    }
                    if (!pkg.protectedBroadcasts.contains(name)) {
                        pkg.protectedBroadcasts.add(name.intern());
                    }
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("instrumentation")) {
                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }

            } else if (tagName.equals("original-package")) {
                  // 解析<original-package> 標(biāo)簽  
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);

                String orig =sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
                if (!pkg.packageName.equals(orig)) {
                    if (pkg.mOriginalPackages == null) {
                        pkg.mOriginalPackages = new ArrayList<String>();
                        pkg.mRealPackage = pkg.packageName;
                    }
                    pkg.mOriginalPackages.add(orig);
                }

                sa.recycle();

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("adopt-permissions")) {
               // 解析<adopt-permissions> 標(biāo)簽  
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestOriginalPackage);

                String name = sa.getNonConfigurationString(
                        com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

                sa.recycle();

                if (name != null) {
                    if (pkg.mAdoptPermissions == null) {
                        pkg.mAdoptPermissions = new ArrayList<String>();
                    }
                    pkg.mAdoptPermissions.add(name);
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("uses-gl-texture")) {
                // 解析<uses-gl-texture> 標(biāo)簽  
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;

            } else if (tagName.equals("compatible-screens")) {
                 // 解析<compatible-screens> 標(biāo)簽  
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;
            } else if (tagName.equals("supports-input")) {
                // 解析<supports-input> 標(biāo)簽  
                XmlUtils.skipCurrentTag(parser);
                continue;

            } else if (tagName.equals("eat-comment")) {
                  // 解析<eat-comment> 標(biāo)簽  
                // Just skip this tag
                XmlUtils.skipCurrentTag(parser);
                continue;

            } else if (RIGID_PARSER) {
                outError[0] = "Bad element under <manifest>: "
                    + parser.getName();
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return null;

            } else {
                Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                        + " at " + mArchiveSourcePath + " "
                        + parser.getPositionDescription());
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
        }

        if (!foundApp && pkg.instrumentation.size() == 0) {
            outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
        }

        final int NP = PackageParser.NEW_PERMISSIONS.length;
        StringBuilder implicitPerms = null;
        for (int ip=0; ip<NP; ip++) {
            final PackageParser.NewPermissionInfo npi
                    = PackageParser.NEW_PERMISSIONS[ip];
            if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
                break;
            }
            if (!pkg.requestedPermissions.contains(npi.name)) {
                if (implicitPerms == null) {
                    implicitPerms = new StringBuilder(128);
                    implicitPerms.append(pkg.packageName);
                    implicitPerms.append(": compat added ");
                } else {
                    implicitPerms.append(' ');
                }
                implicitPerms.append(npi.name);
                pkg.requestedPermissions.add(npi.name);
            }
        }
        if (implicitPerms != null) {
            Slog.i(TAG, implicitPerms.toString());
        }

        final int NS = PackageParser.SPLIT_PERMISSIONS.length;
        for (int is=0; is<NS; is++) {
            final PackageParser.SplitPermissionInfo spi
                    = PackageParser.SPLIT_PERMISSIONS[is];
            if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
                    || !pkg.requestedPermissions.contains(spi.rootPerm)) {
                continue;
            }
            for (int in=0; in<spi.newPerms.length; in++) {
                final String perm = spi.newPerms[in];
                if (!pkg.requestedPermissions.contains(perm)) {
                    pkg.requestedPermissions.add(perm);
                }
            }
        }

        if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
        }
        if (supportsNormalScreens != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
        }
        if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
        }
        if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.GINGERBREAD)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
        }
        if (resizeable < 0 || (resizeable > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
        }
        if (anyDensity < 0 || (anyDensity > 0
                && pkg.applicationInfo.targetSdkVersion
                        >= android.os.Build.VERSION_CODES.DONUT)) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
        }

        return pkg;
    }

開始解析包名了,之后詳細(xì)解析了AndroidManifest下面的每一個(gè)節(jié)點(diǎn),可以看到有application、overlay、key-sets、permission-group,permisstion,permission-tree,uses-permission等等節(jié)點(diǎn)。

我們熟悉的Android四大組件 Activity、Service等都在application節(jié)點(diǎn)之下。那么我們來看下這個(gè)解析application的內(nèi)容。

上一篇文章 APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)
下一篇文章 APK安裝流程詳解10——PackageParser解析APK(下)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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