PackageManagerService启动详解(五)之Android包信息体和解析器(中)

2023-05-16

    PKMS启动详解(五)之Android包信息体和包解析器(中)


Android PackageManagerService系列博客目录:

PKMS启动详解系列博客概要
PKMS启动详解(一)之整体流程分析
PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?
PKMS启动详解(三)之BOOT_PROGRESS_PMS_START流程分析
PKMS启动详解(四)之Android包信息体和包解析器(上)
PKMS启动详解(五)之Android包信息体和包解析器(中)
PKMS启动详解(六)之Android包信息体和包解析器(下)
PKMS启动详解(七)之BOOT_PROGRESS_PMS_SYSTEM_SCAN_START阶段流程分析
PKMS启动详解(八)之BOOT_PROGRESS_PMS_DATA_SCAN_START阶段流程分析



引言

  在前面的博客PKMS启动详解(四)之Android包信息体和包解析器(上)中我们重点从Android包管理机制的设计者角度出发,着重分析了:

  • Android包管理机制中的Android包信息体设计思想和源码逻辑
  • Android包管理机制中的Android包解析器PackageParser设计思想

那么在今天的博客中,我们将再接再厉,一鼓作气的继续分析Android包解析器PackageParser源码实现逻辑!

在正式开始本篇的博客相关分析前,还是非常有必要放出包解析器PackageParser的核心结构图(前面是借用其他博主的,这里就自己花费些时间单独画了):
在这里插入图片描述
同时本篇博客对于包解析器PackageParser类源码,会遵从如下几个方面来进行入手来分析(也可以人为是本篇博客的中心):
1.从包管理机制设计者意图了解它,即它的概述和定义是什么

2.它作为一个工具类,它的核心成员变量是什么(当然是和包信息体的封装有关)

3.它作为一个工具类,它的核心方法有那些(核心方法就是解析Android包)

注意:本篇的介绍是基于Android 7.xx平台为基础的(并且为了书写简便后续PackageManagerService统一简称为PKMS),其中涉及的代码路径如下:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

frameworks/base/core/java/android/content/pm/PackageParser.java



一.从源码的角度概述Android包解析器PackageParser

在前面的博客中,我们从理论上知道了包解析器PackageParser是Android包管理机制设计的一个用于解析Android包的工具类,但是对于它的具体实现我们知之甚少,从这个章节开始我们就从源码角度出发对其展开猛烈的攻势,争取将其一举拿下!实干出真知!

  通常了解一个人首先是从看脸开始(也不排除其它的位置啊,哈哈!),而看Android源码类通常可以从设计者着对它的概述开始,我们这里对于包解析器PackageParser的深入了解也不能免俗的遵循这个定理,我们看看设计者是如何对它进行定义的,如下:

// 【 PackageParser.java】

/**
* 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. 

* Apps packaged as multiple APKs always consist of a single "base" APK (with a 
* 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: 
* 	All APKs must have the exact same package name, version code, and signing certificates. 
* 	All APKs must have unique split names. 
* 	All installations must contain a single base APK. 
* 
* @hide
*/


public class PackageParser {
	...
}

对于上述的描述,英文比较好的读者估计连蒙带猜基本能内部消化了,对于不能内部消化的只能且听我来分解了:

上述的注释非常经典没有过多的拖泥带水,我们对其强撸一番!
1.PackageParser是用于磁盘上包文件(APKs)的解析器。它即支持打包成单个“单块”APK的应用程序的解析,同时也支持打包成多个APK在单个目录中的“集群”应用程序的解析

2.一个"集群"APK有一个"基准"APK(base APK)组成和其他一些"分割"APK(“split” APKs)构成,其中这些"分割"APK用一些数字来分割。这些"分割"APK的必须都是有效的安装,同时必须满足下面的几个条件:

  • 所有APKs必须具有完全相同的包名、版本代码和签名证书。
  • 所有APK必须有唯一的拆分名称。
  • 所有安装必须包含一个单一的APK

3.并且该类是一个hide类,说明它只能用于Android内部使用,不能被第三方应用使用


1.1 Android安装包的形态

在前面的注释中,多次提到了single APK和multiple APKS这两个概念,那么它们纠结表示什么呢!我们们分别来说(其实从单复数就说明情况了)。

1.1.1 Single APK

对于Single APK就比较好理解了,它通常所开发的APK,即每一个应用安装包有且只存在一个.apk文件,形如下面的形式:

xxx:/system/system_ext/priv-app/Settings $ ls
Settings.apk

1.1.2 Multiple APKS

在Android L(5.0)以后,支持APK拆分,即一个APK可以分割成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK文件具备相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK,Android称其为signle APK;对于拆分后的APK,Android称其为multiple APKS。此时形如下面的形式:

xxx:/system/system_ext/priv-app/Settings $ ls
Settings-base.apk Settings-split.apk

对于有过一定Android开发经验的读者一定知道在Android L(5.0)以前,APK文件都是直接位于app或priv-app目录下,譬如短彩信APK的目录就是/system/priv-app/Mms.apk;到了Android L(5.0)之后,多了一级目录结构,譬如短彩信APK的目录是/system/priv-app/Mms/Mms.apk,这是Android为了支持APK拆分而做的改动,如果要将短彩信APK进行拆分,那所有被拆出来的APK都位于/system/priv-app/Mms/即可,这样在包解析时,就会变成以multiple APKS的方式解析目录。

并且这里有一点需要注意,对于/system/priv-app/Mms/下面的安装包,无论他是否multiple APKS形式的,最后都会调用parseClusterPackage方法来进行解析,这个读者一定要注意,注意!千万不要把Multiple APKS和Cluster解析混淆了!它们之间的前提是Multiple APKS一定是采取Cluster解析方式,但是采取Cluster不一定是Multiple APKS也可能是Single APK。

关于Multiple APKS详细介绍可以参看谷歌官网多APK支持!

1.1.3 不同安装包的解析方式

对于Android安装包的形式我们理解清楚了,从而会导致包解析器PackageParser对于上述二者会采取不同的解析方式,但是最后都会徐途同归(至于怎么具体解析的,后续详细分析)!

在这里插入图片描述


并且这里再次强调一点,对于Single APK安装包采取parseMonolithicPackage()还是parseClusterPackage()的解析方式,这个取决于安装包的路径是否是一个文件夹!注意,注意,注意!


1.2 包解析器PackageParser解析Android包的步骤

通过前面的努力我们知道了,PackageParser的核心功能就是解析各种类型的Android包(其实也就两种而已),而解析包的最最主要逻辑就是解析APK文件,在正式开始PackageParser解析Android包之前,这里我们先给出解析一个APK主要是分为两个步骤:

  • 将APK解析成Package数据结构:即解析APK文件为Package对象的过程。

    此处是我们今天博客的重点!

  • 将解析得到的Package填充为PackageInfo数据结构:即由Package对象生成Package对象生成PackageInfo包信息体的过程

    关于Android包信息体我们在前面的博客中已经重点的介绍过,重点关注它是怎么被填充的




二.PackageParser重要成员

  对于PackageParser我们该了解的不该了解的都了解了,是时候对其展开真刀真枪的实干了。这里我们先从其成员(或者是内部类)入手展开!在PackageParser中定义了非常多的内部类用来解析PackageParser,如下所示:

在这里插入图片描述


2.1 IntentInfo内部类

通过前面的博客总结我么可知,对于APK中的androidmanifest.xml的<activity>,<service>标签,都可以配置<intent-filter>,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以ComponentIntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。

所以这里我们就来看下IntentInfo的实现。

没有啥好说的,直接来看源码!

// 【 PackageParser.java 】
public class PackageParser {
	...
	/*
		这里可以看到IntentInfo就是对IntentFilter意图过滤器的再次封装,
		它是承载包组件<intent-filter>标签的数据结构
	*/
    public static class IntentInfo extends IntentFilter {
        public boolean hasDefault;
        public int labelRes;
        public CharSequence nonLocalizedLabel;
        public int icon;
        public int logo;
        public int banner;
        public int preferred;
    }
    ...
}

这里这里可以看到IntentInfo就是对IntentFilter意图过滤器的再次封装,它是用来承载包组件<intent-filter>标签的数据结构。最终我们会得到如下的类图:

在这里插入图片描述
最后我们补充一点,对于IntentFilter我想读者肯定很熟悉了,它就是江湖人称的意图过滤器,通常它的过滤规则过滤规则包含以下三个方面:

  • Action: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Action需要匹配其中至少一个。
  • Category: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Category必须是IntentFilter所定义的Category的子集,才能通过IntentFilter。譬如Intent设置了两个Category:CATEGORY_LAUNCHER和CATEGORY_MAIN,只有那些至少定义了这两项Category的IntentFilter,才会放行该Intent。

    启动Activity时,会为Intent设置默认的Category,即CATEGORY_DEFAULT。目标Activity需要添加一个category为 CATEGORY_DEFAULT的IntentFilter来匹配这一类隐式的Intent。
  • Data:每一个IntentFilter可以定义零个或多个,数据可以通过类型(MIMEType)和位置(URI)来描述,如果Intent想要通过这个IntentFilter,则Intent所辖的Data需要匹配其中至少一个。

2.2 Component内部类

通过前面的博客总结我么可知,一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。在具体捯饬相关组件前,我们先来看下高层的基类Component组件它是怎么设计并且用代码实现的。

无需多言,直接开撸源码!

// 【 PackageParser.java 】
public class PackageParser {
	...
    public static class Component<II extends IntentInfo> {
        public final Package owner;// 该组件依赖的包
        public final ArrayList<II> intents;// 该组件所包含的IntentFilter
        public final String className;// 组件的类名
        public Bundle metaData;// 组件的元数据

        ComponentName componentName;// 组件信息(组件所在的包名和类)
        String componentShortName;// 组件简称
   }
   ...
}

Component被设计成为一个基类,是Android包组件的众多组件的基类数据结构类,然后各个组件类继承并扩展它。并且Android包中每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。最终通过我们如上一番分析,我们会得到如下的类图:

在这里插入图片描述


2.3 XXXIntentInfo内部类

  在前面的章节2.1 IntentInfo内部类中我们有说到了包管理设计者从架构层次出发设计了一个数据结构基类IntentInfo来表示组件可以过滤接收的Intent信息。而Android包管理中具体到不同的组件,会继承它扩展它,这里共有如下三个扩展类:

  • ActivityIntentInfo
  • ServiceIntentInfo
  • ProviderIntentInfo

细心的读者可以留意了,Android常见的四大组件不是四个吗,上述定义的XXXIntentInfo为啥只有三个啊!好吗,实际原因是receiver广播组件和Activity复用了同一个类ActivityIntentInfo而已。

接下来我们简单来看下上述的几个类的源码实现,如下:

public class PackageParser {
	...
	
    public final static class ActivityIntentInfo extends IntentInfo {
    	/*
    		注意此处的Activity是PackageParse的内部类,
    		而不是我们通常意义上的Activity.java对应的Activity
    	*/
        public final Activity activity;
        public ActivityIntentInfo(Activity _activity) {
            activity = _activity;
        }
		...
    }
	

    public final static class ServiceIntentInfo extends IntentInfo {
        /*
	   		注意此处的Service是PackageParse的内部类,
	   		而不是我们通常意义上的Service.java对应的Service
   		*/
        public final Service service;

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

    public static final class ProviderIntentInfo extends IntentInfo {
        /*
	   		注意此处的Service是PackageParse的内部类,
	   		而不是我们通常意义上的Service.java对应的Service
   		*/    
        public final Provider provider;

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

}

上述的源码比较简单,就是Android包的组件根据实际意图情况继承扩展了IntentInfo 最终通过我们如上一番分析,我们会得到如下的类图:

在这里插入图片描述


2.4 Component子类组件实现

  通过前面的博客总结我么可知,一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。什么是组件呢?AndroidManifest.xml文件中所定义的的一些标签,就是组件,譬如<activity>,<service>,<provider>,<permission>等,这些标签分别对应到包解析器中的一个数据结构,它们各自有自身的属性。并且这些组件我们可以将其归纳为如下两大类:

  • Android四大组件为代表的Component子类组件:

    • Activity:注意此Activity是表示包组件的<activity>和<receiver>而不是Activity.java类中的Activity
    • Service:注意此Service是表示包组件的<service>而不是Service.java类中的Service
    • Provider:注意此Porvider是表示包组件的<service>而不是Service.java类中的Service
  • 其它组件类型的的Component子类组件:

    • Instrumentation: 对应包组件<instrumentation>,这个组件通常用于自动化测试的时候使用
    • Permission:对应包组件<permission>,这个组件没有好说的了,比较常见
    • PermissionGroup:对应包组件<permission-group>,存储权限组的相关信息

接下来我们简单来看下上述的几个类的源码实现,如下:

// 【 PackageParser.java 】
public class PackageParser {
	...
    public final static class Activity extends Component<ActivityIntentInfo> {
        public final ActivityInfo info;//此处的info指向的前面博客我们介绍的包信息体类ActivityInfo 

		/*
			这里的参数args表示的是解析包中单个组件的参数类,后续会简单介绍下
		*/
        public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
			...
        }        
    }

    public final static class Service extends Component<ServiceIntentInfo> {
        public final ServiceInfo info;//此处的info指向的前面博客我们介绍的包信息体类ServiceInfo

        public Service(final ParseComponentArgs args, final ServiceInfo _info) {
			...
        }
    }

    public final static class Provider extends Component<ProviderIntentInfo> {
        public final ProviderInfo info;//此处的info指向的前面博客我们介绍的包信息体类ProviderInfo 
        public boolean syncable;

        public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
			...
        }
	}

    public final static class Instrumentation extends Component<IntentInfo> {
        public final InstrumentationInfo info;//此处的info指向的前面博客我们介绍的包信息体类InstrumentationInfo
		/*
			这里的参数args表示的是解析包中包单个item的参数基类,后续会简单介绍下
		*/
        public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
			...
        }	
   }

    public final static class Permission extends Component<IntentInfo> {
        public final PermissionInfo info;//此处的info指向的前面博客我们介绍的包信息体类PermissionInfo 
        public boolean tree;
        public PermissionGroup group;

        public Permission(Package _owner) {
			...
        }
        public Permission(Package _owner, PermissionInfo _info) {
			...
        }
    }

    public final static class PermissionGroup extends Component<IntentInfo> {
        public final PermissionGroupInfo info;//此处的info指向的前面博客我们介绍的包信息体类PermissionGroupInfo

        public PermissionGroup(Package _owner) {
			...
        }

        public PermissionGroup(Package _owner, PermissionGroupInfo _info) {			...
        }
    }
}

上述的源码比较简单,就是Android包的组件根据实际组件情况继承扩展了Component基类,最终通过我们如上一番分析,我们会得到如下的类图:

在这里插入图片描述

2.5 内部类ParsePackageItemArgs和ParseComponentArgs

分析源码就是这么枯燥且乏味的一件事件,蜻蜓点水吗不能深入!太深入了,又限于细节不能自拔了。所以对于一些细节型的类,我们就简单过下!

ParsePackageItemArgs和ParseComponentArgs是一对父子关系类,其设计的用途主要是用来存储Android包组件中额外的一些参数。这个没有啥好说的,简单看下源码:

// 【 PackageParser.java 】
public class PackageParser {
	...

	/*
		解析包单个item的参数
	*/
    static class ParsePackageItemArgs {
        final Package owner; //表示安装包的包对象Package
        final String[] outError;//表示错误信息
        final int nameRes;//表示安装包中组件名字对应的资源id
        final int labelRes;//表示安装包中组件label对应的资源id
        final int iconRes;//表示安装包中组件icon对应的资源id
        final int roundIconRes;//以下类推,不说了
        final int logoRes;//
        final int bannerRes;

        String tag;
        TypedArray sa;

        ParsePackageItemArgs(Package _owner, String[] _outError,
                int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
                int _bannerRes) {
                ...
        }
    }

	/*
		主要为解析包中单个组件的参数,它是ParsePackageItemArgs的子类
	*/
    static class ParseComponentArgs extends ParsePackageItemArgs {
        final String[] sepProcesses;//表示该组件对应的进程,如果设置独立进程则为独立进程的名字
        final int processRes;//表示该组件对应的进程的资源id
        final int descriptionRes;//表示该组件对应的描述id
        final int enabledRes;//表示该组件是否可用
        int flags;//该组件标志位信息

        ParseComponentArgs(Package _owner, String[] _outError,
                int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
                int _bannerRes,
                String[] _sepProcesses, int _processRes,
                int _descriptionRes, int _enabledRes) {
			...
        }
    }
    ...
}

关于二者的介绍,就先这样吗,类图也省了,小芝麻的就不捡起来了!


2.6 内部类NewPermissionInfo和SplitPermissionInfo

Android包解析器中的组件的基类以及子类基本介绍完毕,剩下来的就是一些零碎的一些成员类信息,譬如这个章节要说的NewPermissionInfo和SplitPermissionInfo。

NewPermissionInfo和SplitPermissionInfo见名思意,是和Android组件权限有关的,我们还是简单来看下!

// 【 PackageParser.java 】
public class PackageParser {

    /** @hide */
    /*
    	主要用于记录组件新权限信息
    */    
    public static class NewPermissionInfo {
        public final String name;//权限的名称
        public final int sdkVersion;//表示新权限开始的版本
        public final int fileVersion;//表示文件的版本号,一般为0

        public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
			...
        }
    }

    /** @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) {
        	...
        }
    }
}

关于二者的介绍,就先这样吗,类图也省了,小芝麻的就不捡起来了!


2.7 内部类ApkLite

前面的几个章节中我们分析了Android包组件相关的一些基类和其它的一些零碎类,从这个章节开始我们将要分析的是组织结构更高一个级别关于Android包的一些类的信息。

ApkLite抛开源码字面意思是精简的APK,通过注释看它表示的是轻量级的存储解析独立APK信息的一个数据结构类,我们从源码入手来看下!

// 【 PackageParser.java 】
public class PackageParser {
	...
    /*
    	轻量级的存储解析APK信息的一个数据结构类
    */
    public static class ApkLite {
        public final String codePath;// 表示APK的路径,譬如/system/app/xxx/xxx.apk
        public final String packageName;// 表示应用程序包名
        public final String splitName;	// 表示“拆包”的包名
        public final int versionCode;// 表示版本号
        public final int revisionCode;	// 表示调试的版本号
        public final int installLocation;// 应用安装位置,内置SD卡,外置SD卡等
        /*
			包含包验证过程中PackageManagerService}使用的包验证器的信息
		*/
        public final VerifierInfo[] verifiers;// 校验信息
        public final Signature[] signatures;// 表示签名信息
        public final Certificate[][] certificates;// 证书信息
        public final boolean coreApp;// 是否是核心应用
        public final boolean multiArch;// 是否支持多软件架构,这里主要指的是CPU平台
        public final boolean use32bitAbi;// 是否使用32位系统指令
        public final boolean extractNativeLibs;// 是否依赖额外的本地库

        public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs) {
				...
        }
    }
	...
}

最终经过我们的一顿倒腾,可以得到如下的一个类图:

在这里插入图片描述


2.8 内部类PackageLite

如果说前面的ApkLite是用来封装APK文件的一个数据结构类,那么这里的PackageLite则是用来封装一个“独立的”应用程序包信息轻量级数据结构类!

这里特别注意二者的维度是不同的,一个安装包下可能存在几个APK文件!

PackageLite抛开源码字面意思是精简的安装包信息,通过注释看它表示的是轻量级的存储解析独立安装包信息的一个数据结构类,我们从源码入手来看下!

// 【 PackageParser.java 】
public class PackageParser {
	...
    /**
     * Lightweight parsed details about a single package.
     */
     
    /*
       轻量级的存储应用安装包的一个数据结构类
    **/
    public static class PackageLite {
        public final String packageName;// 表示安装包中base apk应用程序的包名
        public final int versionCode;//表示安装包中base apk应用程序版本号

		/*
			表示应用安装的位置,Android预置了几个常量来选择
			比如PackageInfo.INSTALL_LOCATION_AUTO的值等
		*/
        public final int installLocation;

		/*
			包含包验证过程中PackageManagerService}使用的包验证器的信息
		*/
        public final VerifierInfo[] verifiers;		
        public final String[] splitNames;// 表示安装包有拆包的apk名称
		/*
			表示"代码"的路径,对于"单一APK",则对应的是"base APK"的路径
			如果是"集群APK",则对应的是"集群APK"目录的路径,譬如/system/app/xxx
		*/
        public final String codePath;
        public final String baseCodePath;//表示 base APK的路径,譬如/system/app/xxx/xxx.apk
        public final String[] splitCodePaths;	// 表示"拆分"APK的路径

        public final int baseRevisionCode;// base apk的修订版版本号
        public final int[] splitRevisionCodes;// 拆分a apk的修订版本号信息

        public final boolean coreApp;//是不是核心app
        public final boolean multiArch;// 表示是不是支持多平台,这里主要是指CPU平台
        public final boolean use32bitAbi;// 是否是使用32位abi
        public final boolean extractNativeLibs;// 表示是否依赖额外的本地库

        public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                String[] splitCodePaths, int[] splitRevisionCodes) {
			...
        }
		...
    }	
}

过于其中的每个变量信息,建议读者最好是带入实际情况进行分析,并且最好不要开始就分析有拆包的情况,先得到主干,拆分的情况可以对整体调理清楚之后再行深入!

总之,最终经过我们的一顿倒腾,可以得到如下的一个类图:

在这里插入图片描述


2.8 内部类Package

分析至此,读者有没有一个感觉就是PackageParser包解析器为我们捣鼓了一大堆关于Android包的零部件,都是算乱的遗落在角落各处!有一种十分凌乱拉跨的感觉,难道Android包管理的设计者没有考虑拿一个大一点的容器或者收纳箱将那么一大堆零件归纳起来吗?肯定有了,它就是我们将要介绍的Package类,它才是Android包对应的集大成者的一个数据结构类,几乎囊括了Android包的方方面面。我们来看下,Android包管理者是怎么对它进行设计的!

// 【 PackageParser.java 】
public class PackageParser {
	...

    /**
     * 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.
     */

	/*
		这个类其实就是通过解析APK而对应的一个"包"的类,
		这个包代表一个完整的安装包的信息
		这个包可以是一个包由一个"基础"APK和多个"拆分"APK构成
		
	*/
    public final static class Package {		
        public String packageName;//表示包名
        public String[] splitNames;//表示"拆包"的包名,是个数组,每个元素代表一个"拆分"包名
        public String volumeUuid;
        // 表示代码的路径,如果是单个包,则表示"base"的APK的路径,如果是"集群"包,则表示的"集群"包的目录
        public String codePath;
        public String baseCodePath;// base APK的路径
        public String[] splitCodePaths;// 拆分 APK的路径
        public int baseRevisionCode;// base APK的调整版本号
        public int[] splitRevisionCodes;// 拆分APK的调整版本号
        public int[] splitFlags; // "拆分APK"的标志数组
        public int[] splitPrivateFlags; // "拆分APK"的标志数组
        public boolean baseHardwareAccelerated;// 是否支持硬件加速


        // 对应ApplicationInfo对象 对应AndroidManifest里面的Application
        public final ApplicationInfo applicationInfo = new ApplicationInfo();

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

		// 安装包中AndroidManifest里面的<PermissionGroup>对应的数据存储结构
		public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);

		// 存储Android包组件相关信息
		public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
         /*
        	存放APK安装包中配置文件AndroidManifest.xml里面的<service>标签信息对应的数据结构类Service的列表,
        	关于Service在掐面博客中国已经有介绍了
        */
        public final ArrayList<Service> services = new ArrayList<Service>(0);
        
        /*
        	存放APK安装包中配置文件AndroidManifest.xml里面的<instrumentation>标签信息对应的数据结构类Instrumentation的列表,
        	关于Instrumentation在掐面博客中国已经有介绍了
        */
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);



		public final ArrayList<String> requestedPermissions = new ArrayList<String>();	// 安装包中请求的权限		
		public ArrayList<String> protectedBroadcasts;// 安装包中广播受保护的的Action
        public Package parentPackage;//父包信息
        public ArrayList<Package> childPackages;//子包信息
		
        public ArrayList<String> libraryNames = null;// 安装包需要依赖的库	
        public ArrayList<String> usesLibraries = null;// 安装包中使用的库
        public ArrayList<String> usesOptionalLibraries = null;	// APK安装包中使用选项库的名字	
        public String[] usesLibraryFiles = null;// 安装包中使用库的路径

		// 在AndroidManifest里面的<preferred>标签意图信息集合
        public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;		
        public ArrayList<String> mOriginalPackages = null;// APK安装包中AndroidManifest中对应"original-package"的集合	
        public String mRealPackage = null;//真实包名,通常和mOriginalPackages一起使用	
        public ArrayList<String> mAdoptPermissions = null;	// APK安装包中AndroidManifest中对应"adopt-permissions"集合	
		public Bundle mAppMetaData = null;// 我们独立存储应用程序的元数据,以避免多个不需要的引用
   
        public int mVersionCode;  // 版本号     
        public String mVersionName;     // 版本名  
        public String mSharedUserId;// 共享id
        public int mSharedUserLabel;//共享用户标签
        public Signature[] mSignatures;// 签名信息
        public Certificate[][] mCertificates;// 证书信息

        public int mPreferredOrder = 0;

        public long[] mLastPackageUsageTimeInMills =
                new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];//最后package被使用时间

        //附件数据
        public Object mExtras;

        // 硬件配置信息,对应AndroidManifest里面的<uses-configuration>标签信息
        public ArrayList<ConfigurationInfo> configPreferences = null;

        //特性信息,对应AndroidManifest里面的<uses-feature>标签信息
        public ArrayList<FeatureInfo> reqFeatures = null;
 
         //特性组信息,对应AndroidManifest里面的<feature-group>标签 信息
        public ArrayList<FeatureGroupInfo> featureGroups = null;

        public int installLocation;// 安装的位置	
        public boolean coreApp;		//是否是核心应用
        public boolean mRequiredForAllUsers; // 是否是全局必要,所有用户都需要的应用程序,无法为用户卸载
        public String mRestrictedAccountType;// 受限账户的 验证类型
        public String mRequiredAccountType;//  是否是受信任的Overlay

        public String mOverlayTarget;
        public int mOverlayPriority;
        public boolean mTrustedOverlay;

        // 下面是用来给KeySetManagerService的数据
        public ArraySet<PublicKey> mSigningKeys;// 签名
        public ArraySet<String> mUpgradeKeySets;
        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; // 公钥信息

        public String cpuAbiOverride;//如果有abi,则abi覆盖
        public boolean use32bitAbi;//是否是使用32位abi

        public byte[] restrictUpdateHash;
        ...
   }
   ...
}

上面的有些注释是我直接翻译过来的,所以有些指代不是很明确,这个各位见谅!因为Pakcage的成员变量太多了,关于各个成员的具体指代只能强烈建议读者在学习的时候最好是自己代入实际情况,然后自行进行相关的验证是再好不过的了。

并且还是我开头说的,在这分析的过程中不要代入拆包的这种情况进来,否则分析起来就会很麻烦造成主干,细节纠缠不清!

通过注释和源码我们可以知道Package是对Android包的集大成者的一个数据结构类,它是通过解析APK而对应的一个"包"的大类,这个包代表的就是一个磁盘上的APK安装包所有的信息。总之经过我们一顿猛虎一半的操作,我们最终会得到如下的一个Android包机制的大类图,如下:

在这里插入图片描述




三.PackageParser重要方法

  如果说前面我们分析的PackageParser成员变量等相关的信息,是包管理器为了烹饪Android包准备的各种顶级食材的话,那么PackageParser中定义的各种方法就是为了烹饪Android包准备的各种烹饪技术了。是不是很好奇,Android包管理器是怎么将前面的各种食材进行烹饪得到各种美味呢?这也是本章节和后续博客重点要分析的内容!

这里我回过头来一看,好家伙关于PackageParser中的成员相关的介绍就捣鼓了蛮多了,如果再将其中的方法也放在一起来分析,估计读者和我自己都要奔溃了!所以这里我先简单介绍下PackageParser中重要的方法(方法详细的实现在后续博客介绍),如下:

  • parsePackage()
  • parseClusterPackage()
  • parseMonolithicPackage()
  • parsePackageLite()
  • parseClusterPackageLite()
  • parseMonolithicPackageLite()
  • parseApkLite()

好了,这里只简单的将PackageParser核心方法摆了出来,由于篇幅和排版限制就不在这篇博客中放出来了,在接下来中会专门来一篇博客来分析上述几个核心的方法看看包管理器是怎么烹饪各种Android包组件,得到美味可口的Android包的。




总结

  好了,到这里PackageManagerService启动详解(四)之Android包信息体和解析器(上)分析就告一段落了,在本篇博客中我们重点阐述了如下知识点:

  • Android包管理机制中的Android包解析器设计思想,以及它的重要成员等相关信息

但是由于篇幅,这里就将Android包解析器怎么通过各种方法烹饪它定义的各种成员,得到美味可口的Android包信息放在后续的博客中来进行分析和讲解了!

好了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!未完待续,欢迎读者继续关注。“关不掉的收音机”下一篇PKMS启动详解(六)之Android包信息体和包解析器(下)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PackageManagerService启动详解(五)之Android包信息体和解析器(中) 的相关文章

  • VsCode 配置PySide6及测试

    目录 VSCode插件安装安装Python插件安装PySide6插件 PySide6安装PySide6配置VSCode创建UI文件 在这里插入图片描述 https img blog csdnimg cn cbf7cd76d7d84048ab
  • Ubuntu 14.04 Desktop的Raid1安装总结

    Ubuntu 14 04 Desktop的Raid1安装总结 安装基于Ubuntu14 04 Desktop的Raid1 由于采用UEFI GPT方式作为系统启动方式 xff0c 在安装过程中出现了很多异常情况 本文记录安装的过程 安装步骤
  • sem_wait sem_post信号量操作进本函数

    sem wait sem post 信号量的数据类型为结构sem t xff0c 它本质上是一个长整型的数 函数sem init xff08 xff09 用来初始化一个信号量 它的原型为 xff1a extern int sem init
  • 常见gcc编译警告整理(开始)

    1 warning no newline at end of file 在文件最后一行加上回车键 解释 xff1a 在 Rationale for the C99 standard 一文中 xff0c 有C99的相关信息 xff1a A b
  • 对于结构体变量赋值的误区

    以前在使用结构体时没有在结构体变量之间直接赋值 xff0c 今天同事在查看别人的代码时 xff0c 发现有两个结构体变量直接赋值的语句当时感觉这个语句不对 xff0c 认为在一个结构体里边 xff0c 既有一般的无符号整形与数组 xff0c
  • 线程同步(互斥锁与信号量的作用与区别)

    信号量用在多线程多任务同步的 xff0c 一个线程完成了某一个动作就通过信号量告诉别的线程 xff0c 别的线程再进行某些动作 xff08 大家都在semtake的时候 xff0c 就阻塞在 哪里 xff09 而互斥锁是用在多线程多任务互斥
  • 误解程序运行(从单片机到开始)

    误解程序运行 从单片机到开始 关于程序的执行 xff0c 以前想的不多 xff0c 没有意识到一个程序在运行时 xff0c 从哪里读指令 xff0c 数据又写在哪里 最近在看CSAPP时这个念头经常在脑袋中晃荡 从单片机上知道 xff0c
  • ssh配置key后提示Server refused our key

    文章目录 1 确认问题2 解决问题 1 确认问题 参考 xff1a Putty Getting Server refused our key Error 为了确认ssh具体的错误 xff0c 可以去 etc ssh sshd config中
  • Out-of-Bounds Memory References and Buffer Overflow

    callee pushl edp save edp on stack movl esp edp pushl ebx save ebx subl 20 esp popl ebx restore ebx popl edp restore
  • 电子信息工程四年学习之思

    毕业后 xff0c 回顾四年学习历程发现 xff0c 当时以为的明白 xff0c 到现在都是那时的不明白 或许是自己的经历 xff08 参加比赛比较多 xff09 导致了现在的反思 但是 xff0c 回顾那个时候的课程设置 xff0c 却都
  • 将要到来的三大技术革命与联系

    http www csdn net article 2013 02 14 2814128 2013大数据 http www csdn net article 2013 02 15 2814135 bigdata is coming 大数据
  • Keil的常见编译警告

    1 warning 767 D conversion from pointer to smaller integer 解释 xff1a 将指针转换为较小的整数 影响 xff1a 可能造成的影响 xff1a 容易引起数据截断 xff0c 造成
  • 《大数据时代》之后

    现在想想也不记得当时是怎么找到 大数据时代 这本书的 xff0c 好像是在查找数据库方面的书 xff0c 看到亚马逊推荐的书里有这本 xff0c 发现最近才出版的就买一本回来看看 然而这个过程中 xff0c 其实自己已经得到了大数据带来的影
  • 《代码大全》笔记

    最近将去年毕业时 xff0c 大神推荐的 代码大全 看完了 xff08 已经过去一年了 xff0c 要十分感谢推荐 xff0c 还有凤林兄的 深入理解计算机系统 xff09 零零碎碎的时间 xff0c 发现很多东西虽然在书中标记了 xff0
  • 《编程精粹》思之代码与产品

    之前眼中有代码无产品 xff0c 现在眼中有产品有代码 xff0c 什么时候能做到有产品无代码 xff1f 还需要努力 刚开始实习的时候 xff0c 总喜欢在程序中使用 p 43 1 61 而不是p 1 来给入参 xff0c 甚至于用来给定
  • 杂记:pyinstaller 常用参数和可执行文件启动目录的注意事项

    pyinstaller 常用参数 所谓的常用参数 xff0c 就是指 xff1a 不想看详细帮助不关心版本 一来 pip list 就能知道版本 xff0c 不需要专门记 pyinstaller 的参数 二来能用就行 达到生成 exe 的目
  • 关于spring核心配置文件中的各项主要配置

    1 xff1a spring的核心配置文件中的各种配置 spring的核心配置文件的名字 叫做 applicationContext xml xff0c 后期也可以通过配置文件中的配置修改名称 xff0c 在web xml中进行如下配置 x
  • linux安装pgadmin4--postgresql管理工具

    目录 目录 一 下载二 安装三 配置四 总结声明 pip类似RedHat里的yum xff0c 安装起Python包来十分方便 一 下载 1 1 安装pgadmin4 首先确保有python环境 版本均可 xff0c 安装pip span
  • DICOM图像转Nifti格式-SimpleITK

    文章目录 1 SimpleITK实现1 1 代码 2 读图像的meta信息 其实SimpleITK文档里都有 xff0c 直接搬过来 1 SimpleITK实现 1 1 代码 span class token keyword import
  • Linux 压缩(打包)文件夹 tar/zip

    tar 压缩方法 xff1a tar zcvf home xahot tar gz xahot tar zcvf 打包后生成的文件名全路径 要打包的目录 例子 xff1a 把 xahot文件夹打包后生成一个 home xahot tar g

随机推荐