深入分析PMS服务(一)
来源:互联网 发布:淘宝进口零食推荐 知乎 编辑:程序博客网 时间:2024/06/14 21:29
在前面两篇自己动手编译最新Android源码及自己动手调试Android源码中,我们掌握了Android源码的编译以及调试,现在呢,我们在这基础上分析一些源码的实现.首先从PMS服务开始.
PMS服务即PackageManagerService,主要用来进行APK的管理任务.但是今天,我们并不直接分析PMS的源码,而是从一个工具类PackageParse说起.
首先来认识PackageParser类,它主要用来解析手机上的apk文件(支持Single APK和Multiple APK),主要包含两个过程
- APK->Package:解析APK文件为Package对象的过程
- Package->PackageInfo:由Package对象生成PackageInfo的过程
介于不少童鞋不了解Single APK和Multiple APK,在这里做个简单解释:
Single APK是我们通常所开发的APK,即一个应用只有一个apk文件.而Google Play还允许你为一个应用中发布不同的apk文件,这些apk文件适用于不同设备.举例说明,假设你现在开发了一款APP叫做Monkey,但是目前该APP由于体积太大或者其他因素导致不能同时适用于手机和平板,此时你就可将原先的Monkey.apk拆分为了Monkey-Phone.apk和Monkey-Tablet,分别用于运行在Android手机和Android平板,只要保存两者拥有相同的包名,并用相同key进行签名就可以在发布Monkey应用的时候,一同发布Monkey-Phone.apk和Moneky-Tablet.apk,那么这种一个应用拥有多个APK文件的程序就称之为Multiple APK.
更多信息查看官网:multiple-apks
解析APK文件为Package对象
该过程目的是通过解析磁盘上的APK文件来生成与之相关的Package对象.而Pakcage对象是APK经过完整解析之后的结果.
该过程主要涉及到一系列parseXXX()
格式的方法,起始方法是public Package parsePackage(File packageFile, int flags)
,那么我们就从该方法开始分析其流程:
public Package parsePackage(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { //多个apk文件的目录 return parseClusterPackage(packageFile, flags); } else { //单一APK文件 return parseMonolithicPackage(packageFile, flags); } }
该方法接受两个参数packageFile和flags.并根据packageFile是否是文件目录来确定具体的解析流程.通常我们都是Single APK,因此我们重点关注Single APK的解析,不难发现其具体解析过程给委托给parseMonolithicPackage(packageFile, flags)
方法:
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { //如果是核心应用则以更轻量级的方式进行解析后,判断是否是核心应用,非核心应用不执行解析过程 if (mOnlyCoreApps) { final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } } final AssetManager assets = new AssetManager(); try { //调用parseBaseAPK()继续解析操作 final Package pkg = parseBaseApk(apkFile, assets, flags); pkg.codePath = apkFile.getAbsolutePath(); return pkg; } finally { IoUtils.closeQuietly(assets); } }
在该方法中首先通过mOnlyCoreApps属性判断当前系统是不是只解析核心APK,默认是全部解析.至于什么是核心APK后面再说.现在我们继续关注其解析过程.
这里其解析操作继续由parseBaseApk(apkFile, assets, flags)
完成:
private Package parseBaseApk(File apkFile, AssetManager assets, int flags) throws PackageParserException { ... 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); //为AndroidManifest.xml生成xml文件解析器 parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final String[] outError = new String[1]; final Package pkg = parseBaseApk(res, parser, flags, outError); ... pkg.baseCodePath = apkPath; pkg.mSignatures = null; return pkg; } ... }
而真正的解析又是通过该方法的同名函数:parseBaseApk(Resources res, XmlResourceParser parser, int flags,String[] outError)
完成的,为了突出重点,我对其方法进行了简化:
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { //....省略多行代码.... //循环解析AndroidManifest.xml中的元素 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (tagName.equals("application")) { //....省略多行代码,重点关注其中调用的parseBaseApplication()方法 }else if (tagName.equals("overlay")) { //....省略多行代码.... }else if (tagName.equals("key-sets")){ //paseKeySets() }else if (tagName.equals("permission-group")) { parsePermissionGroup(pkg, flags, res, parser, attrs, }else if (tagName.equals("permission")) { //parsePermission }else if (tagName.equals("uses-configuration")) { //....省略多行代码.... }else if (tagName.equals("uses-feature")) { //parseUsesFeature() }else if (tagName.equals("feature-group")) { //....省略多行代码.... }else if (tagName.equals("uses-sdk")) { //....省略多行代码.... }else if (tagName.equals("supports-screens")) { //....省略多行代码.... }else if (tagName.equals("protected-broadcast")) { //....省略多行代码.... }else if (tagName.equals("instrumentation")) { //....省略多行代码.... }else if (tagName.equals("original-package")) { //....省略多行代码.... }else if (tagName.equals("adopt-permissions")) { //....省略多行代码.... } //....省略多行代码.... } //....省略多汗代码.... }
不难发现这里通过很多parseXXX()方法解析相应的数据,比如:parseBaseApplication(),parseKeySets(),parsePermissionGroup(),parseUsesPermission()
等等.
下面,我们重点关注Application标签的解析,即:parseBaseApplication()
方法:
private boolean parseBaseApplication(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, IOException { //....省略对Application元素属性解析多行代码.... //解析Application下的子元素结点,如activity,receiver,service等 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (tagName.equals("activity")) { //....省略多行代码,主要关注parseActivity().... } else if (tagName.equals("receiver")) { //....省略多行代码,主要关注parseActivity().... }else if (tagName.equals("service")) { //....省略多行代码,主要关注parseService().... }else if (tagName.equals("provider")) { //....省略多行代码,主要关注parseProvider().... }else if (tagName.equals("activity-alias")) { //....省略多行代码,主要关注parseActivityAlias()... }else if (parser.getName().equals("meta-data")) { //....省略多行代码,重点关注parseMetaData().... }else if (tagName.equals("library")) { //....省略多行代码.... }else if (tagName.equals("uses-library")) { //....省略多行代码.... }else if (tagName.equals("uses-package")) { //....省略多行代码.... }else{ //....省略多行代码.... } } return true; }
在解析Application下子元素结点时,同样也是通过很多parseXXX()方法来完成的.比如在解析activity结点时是通过parseActivity()
来完成的,其余自行查阅代码.
另外你可能已经注意到对receiver的解析也是通过parseActivity()
实现的.
到此为止,整个为止,解析的整个流程完成,并返回一个Package对象.
附:PackageParser中所有相关解析方法
由Package对象生成PackageInfo
该过程的目的是从Package中提取相关属性,并封装成PackageInfo类型的对象.
该过程主要涉及到一系列generateXXXInfo()
格式的方法,起始方法是generatePackageInfo()
,那么我们就从该方法开始分析其流程:
public static PackageInfo generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, ArraySet<String> grantedPermissions, PackageUserState state) { return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions, state, UserHandle.getCallingUserId()); }
不难看出这里由调用了其同名方法generatePackageInfo(PackageParser.Package p,
来进行继续解析工作:
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
ArraySet<String> grantedPermissions, PackageUserState state, int userId)
public static PackageInfo generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, ArraySet<String> grantedPermissions, PackageUserState state, int userId) if (!checkUseInstalledOrHidden(flags, state)) { return null; } //从Package对象p中取出一系列的属性值用来初始化pi PackageInfo pi = new PackageInfo(); pi.packageName = p.packageName; pi.splitNames = p.splitNames; pi.versionCode = p.mVersionCode; pi.baseRevisionCode = p.baseRevisionCode; pi.splitRevisionCodes = p.splitRevisionCodes; pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.coreApp = p.coreApp; if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { pi.requiredForAllUsers = p.mRequiredForAllUsers; } pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; pi.firstInstallTime = firstInstallTime; pi.lastUpdateTime = lastUpdateTime; if ((flags&PackageManager.GET_GIDS) != 0) { pi.gids = gids; } if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) { //....省略多行代码.... } if ((flags&PackageManager.GET_ACTIVITIES) != 0) { //....省略多行代码,关注generateActivityInfo().... } if ((flags&PackageManager.GET_RECEIVERS) != 0) { //....省略多行代码,关注generateActivityInfo().... } if ((flags&PackageManager.GET_SERVICES) != 0) { //....省略多行代码,关注generateServiceInfo().... } if ((flags&PackageManager.GET_PROVIDERS) != 0) { //....省略多行代码,关注generateProviderInfo().... } if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) { //....省略多行代码,关注generateInstrumentationInfo().... } if ((flags&PackageManager.GET_PERMISSIONS) != 0) { //....省略多行代码,generatePermissionInfo.... } if ((flags&PackageManager.GET_SIGNATURES) != 0) { //....省略多行代码.... } return pi; }
上面的过程主要从Package对象取出一系列的属性用来初始化PackageInfo对象,该过程不再涉及磁盘文件的解析操作.
和解析过程相对,该过程借助了很多generateXXXInfo()
方法来实现.在解析过程中对于Application元素的解析提供了parseApplication()
,而在该过程中也提供了generateApplicationInfo()
来实现Application的取值操作
附:PackageParser中所有相关的generate方法
中途小结
到现在为止,我们已经了解Package的生成和PackageInfo生成,不难发现Package的生成是以磁盘APK文件作为输入,而PackageInfo是以Package对象作为输入.得益于Google工程师良好的设计,PackageParse具有非常好的对称性,非常容易理解.在这里,我只是简单的介绍了该类,对于具体的操作并没有深入的说明,其原因在于,其核心就是通过使用Pull Parser对xml文件进行解析的操作.
附:PackageParser所有内部类:
细心的同学已经发现在上面所示的内部类中也存在Activity,Service等类,要注意这些并不是我们平常使用的Activity组件.PackageParser中的内部类,如Activity,Service,Provider,Permission,皆对应于AndroidManifest.xml文件中的某个标签,用于存储解析出来相关的信息.
这里我们同样用类图来简单的描述期间的关系:
相关实体类
接下来,我们来介绍与上述过程相关的几个实体类,以便你有一个宏观的认识,从而为理解后面的PMS打下基础.
对于这几个实体类,我们值做简单的说明,其具体的点还是需要我们自己进行深究.
Package
PackageParser的静态内部类,代表磁盘上APK文件完整解析后的对象,相当于在内存中Package的对象是对磁盘APK的描述.这里我们只需要关注其属性即可,大部分属性对你而来都是很熟悉的:
public final static class Package { public String packageName; /** Names of any split APKs, ordered by parsed splitName */ public String[] splitNames; //apk文件在磁盘的路径.可能是一个apk的路径,也可能是包含多个apk文件的目录 public String codePath; /** Path of base APK */ public String baseCodePath; /** Paths of any split APKs, ordered by parsed splitName */ public String[] splitCodePaths; /** Revision code of base APK */ public int baseRevisionCode; /** Revision codes of any split APKs, ordered by parsed splitName */ public int[] splitRevisionCodes; /** Flags of any split APKs; ordered by parsed splitName */ public int[] splitFlags; public boolean baseHardwareAccelerated; public final ApplicationInfo applicationInfo = new ApplicationInfo(); //权限 public final ArrayList<Permission> permissions = new ArrayList<Permission>(0); public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0); //四大组件Activity,Receiver,Service,Provider 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); public final ArrayList<Service> services = new ArrayList<Service>(0); public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0); public final ArrayList<String> requestedPermissions = new ArrayList<String>(); public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>(); public ArrayList<String> protectedBroadcasts; public ArrayList<String> libraryNames = null; public ArrayList<String> usesLibraries = null; public ArrayList<String> usesOptionalLibraries = null; public String[] usesLibraryFiles = null; public ArrayList<ActivityIntentInfo> preferredActivityFilters = null; public ArrayList<String> mOriginalPackages = null; public String mRealPackage = null; public ArrayList<String> mAdoptPermissions = null; // We store the application meta-data independently to avoid multiple unwanted references public Bundle mAppMetaData = null; // The version code declared for this package. public int mVersionCode; // The version name declared for this package. public String mVersionName; // The shared user id that this package wants to use. public String mSharedUserId; // The shared user label that this package wants to use. 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. public int mPreferredOrder = 0; // For use by package manager to keep track of where it needs to do dexopt. public final ArraySet<String> mDexOptPerformed = new ArraySet<>(4); // For use by package manager to keep track of when a package was last used. 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. public Object mExtras; // Whether an operation is currently pending on this package public boolean mOperationPending; // Applications hardware preferences public ArrayList<ConfigurationInfo> configPreferences = null; // Applications requested features public ArrayList<FeatureInfo> reqFeatures = null; // Applications requested feature groups 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 */ public boolean mRequiredForAllUsers; /* The restricted account authenticator type that is used by this application */ public String mRestrictedAccountType; /* The required account type without which this application will not function */ public String mRequiredAccountType; /** * 代表一个包文件的摘要,用于确定两个package是否不一致 */ public ManifestDigest manifestDigest; public String mOverlayTarget; public int mOverlayPriority; public boolean mTrustedOverlay; /** * Data used to feed the KeySetManagerService */ public ArraySet<PublicKey> mSigningKeys; public ArraySet<String> mUpgradeKeySets; public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; public String cpuAbiOverride; }
PackageInfo
该类代表包的整体描述信息,即AndroidManifest.xml中的信息.如果说Package在内存中代表完整的APK描述,那么PackageInfo则是其子集,来简单的看一下其代码:
public class PackageInfo implements Parcelable { public String packageName; public String[] splitNames; public int versionCode; public String versionName; public int baseRevisionCode; public int[] splitRevisionCodes; public String sharedUserId; public int sharedUserLabel; public ApplicationInfo applicationInfo; public long firstInstallTime; public long lastUpdateTime; public int[] gids; public ActivityInfo[] activities; public ActivityInfo[] receivers; public ServiceInfo[] services; public ProviderInfo[] providers; public InstrumentationInfo[] instrumentation; public PermissionInfo[] permissions; public String[] requestedPermissions; public int[] requestedPermissionsFlags; public Signature[] signatures; public ConfigurationInfo[] configPreferences; public FeatureInfo[] reqFeatures; public FeatureGroupInfo[] featureGroups;}
对比Package和PackageInfo很容易发现期间的关系,接下来顺便介绍PackageInfo中涉及到的实体类:
<activity>
和<recevier>
元素的信息 ServiceInfo 该实体类代表AndroidManiest.xml中的<service>
元素中的信息 ProviderInfo 该实体类代表AndroidManiest.xml中的<provider>
元素的信息 InstrumentationInfo 该实体类代表AndroidManiest.xml中的<instrumentation>
元素的信息 PermissionInfo 该实体类代表AndroidManiest.xml中的<permission>
元素的信息 ConfigurationInfo 关于程序要求的硬件信息,该实体类代表AndroidManiest.xml中<uses-configuration>
和<uses-feature>
元素的信息. FeatureInfo 该实体类代表AndroidManiest.xml中的<uses-feature>
元素的信息 FeatureGroupInfo 该实体类代表AndroidManiest.xml中的<feature-group>
元素的信息 ManifestDigest 代表一个包文件的摘要信息这里我们用一张类图来描述其类间的关系:
总结
到现在PackageParser的基本解释已经完成,之所以在分析PMS之前先来谈PackageParser的原因在于,该工具类可以脱离上下文,单独进行理解,而无关你目前的状态,这也就避免我们面对一大堆源码,在阅读过程找不到侧重点的问题.接下来,是对PackageManager的分析.
- 深入分析PMS服务(一)
- and5.1PowerManagerService深入分析(一) PMS的初始化以及低功耗模式
- PMS服务之updatePowerStateLocked方法分析
- pms包管理服务分析-初步理解
- Android PackageManagerService分析一:PMS的启动
- Android PackageManagerService分析一:PMS的启动
- PMS 分析
- pms包管理服务分析-证书校验流程
- pms包管理服务分析-apk卸载流程
- (OK) Android PackageManagerService分析一:PMS的启动
- 初探Android PMS服务
- PMS项目需求分析
- 初探Android的PMS服务
- and5.1PowerManagerService深入分析(四)PMS与Display模块
- and5.1PowerManagerService深入分析(四)PMS与Display模块
- pms包管理服务分析-PackageManagerService构造函数和包扫描过程
- pms包管理服务分析-PackageParser.Pacakge, Settings, PackageSettings的结构和关系
- pms包管理服务分析-权限管理和鉴权过程
- RPC 工具 --Thrift(二) Thrift 异步模式
- 编程珠玑第8章总结
- Unity3d扫描 二维码 解析尝试
- 介绍图片的三种格式:GIF、JPEG、PNG
- python笔记4
- 深入分析PMS服务(一)
- Java TPS实现
- tabbaritem字体颜色,大小修改
- php函数笔记
- MYSQL运算符
- 【笔记】PMBOK第4章项目整合管理
- CentOS 6.5 安装 Redis 执行 make #error "Newer version of jemalloc required"
- 二叉树详解及二叉树的前序、中序、后序遍历(递归和非递归)
- C#控制台 虚方法与多级继承的覆写方法