PMS APK解析数据同步和配置更新
前面学到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
在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
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
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 updateAllSharedLibrariesLPw(PackageParser.Package changingPkg) {ArrayList 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 permConfig= systemConfig.getPermissions();for (int i=0; i
遍历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,"", 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进行声明, 比如:
然后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
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
