前面学到PMS构造函数中调用scanDirTraceLI()方法扫描系统app/priv-app/framework等目录下的apk文件,然后调用通过ParallelPackageParser提交(submit())apk给PackageParser解析,PackageParser调用parserPackage()方法解析每一个apk,并将四大组件和其他相关等信息解析出来放入package对象中,然后将所有的package对象放入集合mPackages中。然后PMS的包扫描和解析过程基本上就算是完成了。但扫描和解析完成之后,关于apk的信息都存储在队列中,我们还需要将它们从队列中取出来,同步到PMS的属性中,然后将这些apk的信息在packages.xml和packages.list文件中更新。
        这里我们来学习扫描和解析完成之后,apk数据的同步和配置更新。
        在使用线程池执行所有的apk解析后,所有的解析结果都保存在队列中,系统会循环调用take()方法取出解析的结果。

for (; fileCount > 0; fileCount--) {ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags,currentTime, null);}

1  scanPackageChildLI() 获取扫描到的ParseResult中的Pakcage对象

取出apk文件解析结果后,调用scanPackageChildLI()扫描获取到的ParseResult中的Pakcage对象,scanPackageChildLI()中直接调用addForInitLI()方法.

//addForInitLI()
synchronized (mPackages) {
final PackageSetting installedPkgSetting = mSettings.getPackageLPr(pkg.packageName); // 1、获取从配置文件中读取的Settingsfinal PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); // 调用scanPackageNewLI将pkg中的数据保存到PMS的变量中
}// scanPackageNewLI():创建ScanRequest对象,执行文件扫描修改或更新对应的PackageSetting对象
final ScanRequest request = new ScanRequest(pkg, sharedUserSetting,pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,originalPkgSetting, realPkgName, parseFlags, scanFlags,(pkg == mPlatformPackage), user);
final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
if (result.success) {commitScanResultsLocked(request, result); // 调用commitScanResultsLocked()
}

commitScanResultsLocked()中直接调用commitPackageSettings()处理apk的解析数据 。

commitPackageSettings(pkg, oldPkg, pkgSetting, user, scanFlags,
(parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/); // 提交包解析数据

2    commitPackageSettings()将package信息保存到PMS内部变量中

commitPackageSettings()方法主要是将解析得到的Package中的信息保存到PMS内部变量中,并创建程序包所需的各种文件信息。

private void commitPackageSettings(PackageParser.Package pkg,@Nullable PackageParser.Package oldPkg, PackageSetting pkgSetting, UserHandle user,final @ScanFlags int scanFlags, boolean chatty) {
final String pkgName = pkg.packageName;
if (pkg.packageName.equals("android")) { // 1、针对系统包,特殊处理属性的初始化mPlatformPackage = pkg;pkg.mVersionCode = mSdkVersion;mAndroidApplication = pkg.applicationInfo;mResolveActivity.applicationInfo = mAndroidApplication;mResolveActivity.name = ResolverActivity.class.getName();
......mResolveComponentName = new ComponentName(mAndroidApplication.packageName, mResolveActivity.name);
}int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;for (int i=0; i<N; i++) {String file = mSharedLibraries.get(pkg.usesLibraries.get(i)); mTmpSharedLibraries[num] = file;   // 遍历所有的user-library标签保存在数组中num++;}if (!verifySignaturesLP(pkgSetting, pkg)) { // 校验签名文件…….
}int N = pkg.providers.size();StringBuilder r = null;int i;for (i=0; i<N; i++) {PackageParser.Provider p = pkg.providers.get(i); //遍历提取每个providerp.info.processName = fixProcessName(pkg.applicationInfo.processName,p.info.processName, pkg.applicationInfo.uid);mProvidersByComponent.put(new ComponentName(p.info.packageName,p.info.name), p); // 针对Provider创建ComponentName对象,保存在mProvidersByComponent集合中if (p.info.authority != null) {String names[] = p.info.authority.split(";"); // 获取Provider的权限信息p.info.authority = null;for (int j = 0; j < names.length; j++) {if (j == 1 && p.syncable) {p = new PackageParser.Provider(p);p.syncable = false;}if (!mProviders.containsKey(names[j])) {mProviders.put(names[j], p); // 将权限和对应的Provider以键值对保存在 mProviders 集合中}}}}
N = pkg.services.size();r = null;for (i=0; i<N; i++) {PackageParser.Service s = pkg.services.get(i);s.info.processName = fixProcessName(pkg.applicationInfo.processName,s.info.processName, pkg.applicationInfo.uid);mServices.addService(s);
N = pkg.receivers.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.receivers.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mReceivers.addActivity(a, "receiver");}N = pkg.activities.size();r = null;for (i=0; i<N; i++) {PackageParser.Activity a = pkg.activities.get(i);a.info.processName = fixProcessName(pkg.applicationInfo.processName,a.info.processName, pkg.applicationInfo.uid);mActivities.addActivity(a, "activity");
}

在commitPackageSettings中,主要是将每个apk文件获得的Package对象中保存的四大组件信息分别提取保存在PMS内部对应的属性中,在PMS内部有4个专门储存四大组件的属性:

final ActivityIntentResolver mActivities = new ActivityIntentResolver();
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
final ServiceIntentResolver mServices = new ServiceIntentResolver();
final ProviderIntentResolver mProviders = new ProviderIntentResolver();

1.ActivityIntentResolver.addActivity():处理Activity和Receiver分别保存在各自的ArrayMap中

public final void addActivity(PackageParser.Activity a, String type) {mActivities.put(a.getComponentName(), a); // 1、获取内部的Component对象,在Activity会自动创建Component对象final int NI = a.intents.size();for (int j=0; j<NI; j++) {PackageParser.ActivityIntentInfo intent = a.intents.get(j); // 获取Activity中设置的intentaddFilter(intent); // 添加Intent过滤}
}

2.ServiceIntentResolver.addActivity():将Package中极细获取的Service对象,保存在ArrayMap中

public final void addService(PackageParser.Service s) {mServices.put(s.getComponentName(), s); //保存servicefinal int NI = s.intents.size();int j;for (j=0; j<NI; j++) {PackageParser.ServiceIntentInfo intent = s.intents.get(j);addFilter(intent);}
}

3.ProviderIntentResolver.addActivity():将Package中极细获取的Provider对象,保存在ArrayMap中。

public final void addProvider(PackageParser.Provider p) {if (mProviders.containsKey(p.getComponentName())) {return;}mProviders.put(p.getComponentName(), p); // 保存providerfinal int NI = p.intents.size();int j;for (j = 0; j < NI; j++) {PackageParser.ProviderIntentInfo intent = p.intents.get(j);addFilter(intent); // 添加查找过滤的intent}
}

3  updateAllSharedLibrariesLPw同步共享Library

接着是调用updateAllSharedLibrariesLPw更新所有package的usesLibraryFiles数据,为什么要做更新?因为package在AndroidManifest里头使用use-library描述要引用的library,在系统mSharedLibraries里可能是未定义的,所以,需要做数据同步。app可以在Manifest里头声明自身运行需要额外的library,但是,这里仅仅只是声明,最终还是要看系统有没有配置这个shareLibrary,系统配置的shareLibrary可在初始化SystemConfig时知道,如果app的uses-library在系统中不存在,apk会安装失败。

private ArrayList<PackageParser.Package> updateAllSharedLibrariesLPw(PackageParser.Package changingPkg) {ArrayList<PackageParser.Package> res = null;for (PackageParser.Package pkg : mPackages.values()) {if (changingPkg != null&& !hasString(pkg.usesLibraries, changingPkg.libraryNames)&& !hasString(pkg.usesOptionalLibraries, changingPkg.libraryNames)&& !ArrayUtils.contains(pkg.usesStaticLibraries,changingPkg.staticSharedLibName)) {return null;}if (res == null) {res = new ArrayList<>();}res.add(pkg);try {updateSharedLibrariesLPr(pkg, changingPkg);} catch (PackageManagerException e) {// If a system app update or an app and a required lib missing we// delete the package and for updated system apps keep the data as// it is better for the user to reinstall than to be in an limbo// state. Also libs disappearing under an app should never happen// - just in case.if (!pkg.isSystem() || pkg.isUpdatedSystemApp()) {final int flags = pkg.isUpdatedSystemApp()? PackageManager.DELETE_KEEP_DATA : 0;deletePackageLIF(pkg.packageName, null, true, sUserManager.getUserIds(),flags , null, true, null);}Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());}}return res;}

 4   updateAllPermissions权限处理

调用updateAllPermissions更新系统permission trees和permission 列表

mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),mPermissionCallback);ver.sdkVersion = mSdkVersion;

在PMS构造时,会调用SystemConfig.getPermissions()获取系统builtin权限数据

 // Propagate permission configuration in to package manager.ArrayMap<String, SystemConfig.PermissionEntry> permConfig= systemConfig.getPermissions();for (int i=0; i<permConfig.size(); i++) {SystemConfig.PermissionEntry perm = permConfig.valueAt(i);BasePermission bp = mSettings.mPermissions.get(perm.name);if (bp == null) {bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);mSettings.mPermissions.put(perm.name, bp);}if (perm.gids != null) {bp.gids = appendInts(bp.gids, perm.gids);}}

遍历systemConfig定义的permission时,接着判断mSettings.mPermissions是否已经存在该权限的定义,如果不存在,新建一个BasePermission,权限source package为android,type为builtin,接着更新权限对应的supplementary gids;

扫描apk时权限数据的初始化:

//PackageParser.parsePermission
private Permission parsePermission(Package owner, Resources res,XmlPullParser parser, AttributeSet attrs, String[] outError)throws XmlPullParserException, IOException {Permission perm = new Permission(owner);TypedArray sa = res.obtainAttributes(attrs,com.android.internal.R.styleable.AndroidManifestPermission);if (!parsePackageItemInfo(owner, perm.info, outError,"<permission>", sa,com.android.internal.R.styleable.AndroidManifestPermission_name,com.android.internal.R.styleable.AndroidManifestPermission_labelcom.android.internal.R.styleable.AndroidManifestPermission_icon,com.android.internal.R.styleable.AndroidManifestPermission_logo,com.android.internal.R.styleable.AndroidManifestPermission_banner)) {sa.recycle();mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;return null;}
...owner.permissions.add(perm);return perm;
}

permission和permission tree都保存到Package.permissions列表中,通过字段perm.tree是true还是false来区分是否是permissiontree
在app扫描结束后,permission数据已经被全部拿到,但是现在数据都还只是保存在Package内部,所以还需要将permission数据添加到PMS permission Map。

对大部分app来说,如果要使用某权限,必须要在manifest进行声明, 比如:
<uses-permissionandroid:name="android.permission.INTERNET" />
        然后PackageParser. parseBaseApk时,会调用parseUsesPermission对声明解析使用的权限数据。

5  mSettings.writeLPr()配置更新
        mSettings.writeLPr():将mPackages中的数据分别写入package.xml和package.list文件中

void writeLPr() {if (mSettingsFilename.exists()) {if (!mBackupSettingsFilename.exists()) { // 重命名文件if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {Slog.wtf(PackageManagerService.TAG,"Unable to backup package manager settings, "+ " current changes will be lost at reboot");return;}} else {mSettingsFilename.delete(); //删除package文件}}
FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
BufferedOutputStream str = new BufferedOutputStream(fstr);......for (final PackageSetting pkg : mPackages.values()) {writePackageLPr(serializer, pkg); // 写入配置信息}......
}
writePackageListLPr(); // 更新package.list文件

主要是以下几件事:
1.先判断packages.xml和backup.xml文件是否存在,如果两个都存在则删除packages.xml
2.如果backup.xml文件不存在,则将packages.xml重命名为backup.xml
3.创建新的packages.xml文件,并将mSetting中的内容写入文件夹
4.删除backup文件,并重新生成packages.list文件

至此apk的扫描和解析的数据同步以及配置更新就完成了,关于权限的处理,Android源码的更新比较快,我上述写的方法是Android9的源码中的逻辑,在Android的早些版本的源码中调用的是updatePermissionsLPw这个方法循环遍历mSettings.mPackages集合来将所有的应用赋予权限,而最后的writeLPr方法则是将一些应用信息保存到packages.xml文件中。

参考链接:https://blog.csdn.net/Alexwll/article/details/102777742

PMS APK解析数据同步和配置更新相关推荐

  1. 实现私有DNS搭建(正向解析+反向解析+主从同步+安全措施+配置转发服务器+基于ACL实现智能DNS)

    先了解一些bind中常用工具: dig :DNS 查找工具 描述: dig是一个用于查询DNS名称服务器的灵活工具.它执行DNS查找并显示从所查询的名称服务器返回的答案.大多数DNS管理员使用dig来 ...

  2. mysql换服务器后数据同步_mysql配置主从,主服务器之前的数据可不可以同步过来...

    Slave_SQL_Running: No mysql同步故障解决 如果数据不同步可以尝试该资料 mysql> show slave status\G Slave_IO_Running: Yes ...

  3. sersync+rsync 数据同步配置

    目标:  在master 主机上写入数据后,master 利用sersync 监控本地数据目录,当发生变化时,触发命令,使用rsync推送变化的数据到远程的slave主机上,实现数据同步. 配置思路: ...

  4. 高效数据同步工具DataX的使用

    一.DataX 简介 DataX 是阿里云 DataWorks 数据集成 的开源版本,主要就是用于实现数据间的离线同步. DataX 致力于实现包括关系型数据库(MySQL.Oracle 等).HDF ...

  5. MetaQ 简单使用(数据同步框架)

    数据同步通用框架说明 框架说明 数据同步工具用来对两个系统或者多个系统的数据进行同步处理,框架能够实现项目开发人员只需配置两边系统需的业务数据,不需要关系数据是如何实现同步的,框架底层数据传输技术使用 ...

  6. python 数据库同步_python实现不同数据库间数据同步功能

    功能描述 数据库间数据同步方式很多,在上篇博文中有总结.本文是用py程序实现数据同步. a数据库中有几十张表,要汇聚到b数据库中,且表结构一致,需要准实时的进行数据同步,用工具实现时对其控制有限且配置 ...

  7. MYSQL数据同步到ES7

    ** 概述 ** 现在的项目数据量越来越大,全文检索功能使用场景也越来越普遍. 而我们一般的生产数据是在mysql,或其它一些数据库, 我们的产品数据就是mysql,而又要使用全文检索, 所以要把my ...

  8. SAP CRM和C4C数据同步的两种方式概述:SAP PI和HCI

    SAP Cloud for Customer(C4C)和SAP其他传统产品进行数据同步的方式,如下图所示,可以使用SAP Netweaver Process Integration或者SAP HANA ...

  9. mysql数据库版本不同_MySQL不同版本数据同步

    1.环境准备: 系统环境:CentOS Linux release 7.7.1908 (Core) MySQL环境: 172.16.6.140: Ver 5.0.96-community-log fo ...

最新文章

  1. 【Python】青少年蓝桥杯_每日一题_3.11_体重指数
  2. 超级直播sop直播源.zip_超级直播app壳 打造自己的直播app
  3. poj-Scout YYF I
  4. sql2008“备份集中的数据库备份与现有的xx数据库不同”解决方法
  5. D3D中的粒子系统(4)
  6. java html entity encoding,实体“HTML.Version”的声明必须以''结尾
  7. 借钱不还,法院可以单方拍卖房产吗?
  8. c mysql日期时间格式_mysql日期和时间类型
  9. JAVA输出菱形并使用绝对值,案例用绝对值的方法打印出菱形
  10. AFNetWorking 对汉字部分UTF-8编码
  11. php查看音频属性,PHP获取音频mp3文件时长或音频文件其它参数属性
  12. sublime text 2 学习(一):快捷键
  13. seo查询系统php源码,2020版SEO计费系统的源码
  14. 计算机虚拟仪器技术与测试技术相关的概念,虚拟测试技术概念辨析.pdf
  15. 181021词霸有道扇贝每日一句
  16. 几款常用的文献管理软件
  17. linux下安装mysql
  18. DOS/WinPE双启动移动硬盘制作详解
  19. 江苏省普通高校“专转本”选拔考试 计算机专业大类专业综合基础理论考试大纲
  20. js获取元素自身的html代码

热门文章

  1. 关于PowerDesigner 15/12.5 生成自增字段的问题
  2. 关于确界的一些习题与结论
  3. 裸机服务器安装centos7新手心酸历程
  4. 什么是耦合、紧耦合、松耦合
  5. (每日一练java)CC12 拆分词句
  6. anaconda目录下的pkgs文件夹很大,可以删除吗?
  7. 基于区块链的甘薯质量安全追溯系统设计
  8. 百度AI 图片识物/ 图片转文字(JAVA)
  9. VBS 的回车换行符
  10. 使用Marmoset Toolbag八猴渲染器的Marmoset Viewer进行离线本地观察