Android 11 PackageManagerService源码分析(二):Packages.xml详解

2024-08-25 13:08

本文主要是介绍Android 11 PackageManagerService源码分析(二):Packages.xml详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、开篇

在上一篇文章中提到Settings类会在PackageManagerService启动过程中对packages.xml等一些列xml文件进行解析。那么有以下问题:

  1. 这些文件记录了什么内容?
  2. 为什么需要这些文件?

让我们一起通过阅读源码解决这些问题吧。

2、packages.xml文件详解

要在真机上拿到packages.xml殊为不易,所以我这里是在模拟器上通过adb命令拉取了一份:

adb pull /data/system/packages.xml

文件内容精简后如下:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages><version sdkVersion="24" databaseVersion="3" fingerprint="Android/sdk_phone_x86/generic_x86:7.0/NYC/4174735:userdebug/test-keys" /><version volumeUuid="primary_physical" sdkVersion="24" databaseVersion="3" fingerprint="Android/sdk_phone_x86/generic_x86:7.0/NYC/4174735:userdebug/test-keys" /><permission-trees /><permissions><item name="android.permission.REAL_GET_TASKS" package="android" protection="18" /><item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />...</permissions><package name="com.android.providers.media" codePath="/system/priv-app/MediaProvider" nativeLibraryPath="/system/priv-app/MediaProvider/lib" primaryCpuAbi="x86" publicFlags="944291397" privateFlags="8" ft="15d38697a58" it="15d38697a58" ut="15d38697a58" version="800" sharedUserId="10010"><sigs count="1"><cert index="2" key="308..." /></sigs><perms><item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />...</perms><proper-signing-keyset identifier="4" /></package>...<shared-user name="com.android.emergency.uid" userId="10011"><sigs count="1"><cert index="1" /></sigs><perms><item name="android.permission.MANAGE_USERS" granted="true" flags="0" /></perms></shared-user><keyset-settings version="1"><keys><public-key identifier="1" value="MIIBIDAN..." /><public-key identifier="2" value="MIIBI..." /><public-key identifier="3" value="MIIBI..." /><public-key identifier="4" value="MIIBID..." /></keys><keysets><keyset identifier="1"><key-id identifier="1" /></keyset><keyset identifier="2"><key-id identifier="2" /></keyset><keyset identifier="3"><key-id identifier="3" /></keyset><keyset identifier="4"><key-id identifier="4" /></keyset></keysets><lastIssuedKeyId value="4" /><lastIssuedKeySetId value="4" /></keyset-settings>
</packages>

可以看到,packages.xml主要记录了以下几方面的信息:

  1. 权限信息,permission-trees标签和permissions标签。这里记录的是系统里所有的权限条目
  2. 安装的App信息,包括系统App和用户自行安装的App,package和updated-package标签。其中package标签用户记录一般App的信息;而updated-package通常用于被用户手动升级了的系统App,比如说手机自带了计算器App,这个自带的App的版本是1.0,而厂商又在它的应用商店发布了计算器2.0,当我们在应用商店更新成2.0版本的时候,在系统分区和用户安装分区会各存在一个计算器App。这也是为什么我们在系统应用管理界面删除更新后的计算器2.0后,手机上还是会有计算器1.0版本,而不是彻底消失的原因。
    package标签的属性记录了包名、代码路径、版本等各项信息。另外,package还会有一些子标签,sigs记录App的签名信息,perms记录App的权限信息,注意这里的权限是manifest中声明的权限而不是已经授予的权限。
  3. 共享用户信息,shared-user标签。一般来说一个Android系统会为每一个App分配一个user id,但是我们也可以在manifest元素中指定android:sharedUserId属性,使多个App具有同样的user id从而可以共享数据等等。但是除了系统App,Android强烈建议我们不要使用它,并且在未来的版本可能会被移除,所以了解即可。
  4. 签名信息,keyset-settings标签,记录的是应用签名的公钥。

packages.xml也不止以上这些标签,具体可以参考Settings类对packages.xml的解析:

boolean readLPw(@NonNull List<UserInfo> users) {FileInputStream str = null;if (mBackupSettingsFilename.exists()) {try {str = new FileInputStream(mBackupSettingsFilename);mReadMessages.append("Reading from backup settings file\n");PackageManagerService.reportSettingsProblem(Log.INFO,"Need to read from backup settings file");if (mSettingsFilename.exists()) {// 两者都存在的时候,说明上次更新packages.xml时发生了异常Slog.w(PackageManagerService.TAG, "Cleaning up settings file "+ mSettingsFilename);mSettingsFilename.delete();}} catch (java.io.IOException e) {// We'll try for the normal settings file.}}mPendingPackages.clear();mPastSignatures.clear();mKeySetRefs.clear();mInstallerPackages.clear();try {if (str == null) {if (!mSettingsFilename.exists()) {mReadMessages.append("No settings file found\n");PackageManagerService.reportSettingsProblem(Log.INFO,"No settings file; creating initial state");// It's enough to just touch version details to create them// with default valuesfindOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();return false;}str = new FileInputStream(mSettingsFilename);}XmlPullParser parser = Xml.newPullParser();parser.setInput(str, StandardCharsets.UTF_8.name());int type;while ((type = parser.next()) != XmlPullParser.START_TAG&& type != XmlPullParser.END_DOCUMENT) {;}if (type != XmlPullParser.START_TAG) {mReadMessages.append("No start tag found in settings file\n");PackageManagerService.reportSettingsProblem(Log.WARN,"No start tag found in package manager settings");Slog.wtf(PackageManagerService.TAG,"No start tag found in package manager settings");return false;}int outerDepth = parser.getDepth();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();if (tagName.equals("package")) {readPackageLPw(parser);} else if (tagName.equals("permissions")) {mPermissions.readPermissions(parser);} else if (tagName.equals("permission-trees")) {mPermissions.readPermissionTrees(parser);} else if (tagName.equals("shared-user")) {readSharedUserLPw(parser);} else if (tagName.equals("preferred-packages")) {// 不再使用了,所以不做任何操作} else if (tagName.equals("preferred-activities")) {// Upgrading from old single-user implementation;// these are the preferred activities for user 0.readPreferredActivitiesLPw(parser, 0);} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {// TODO: check whether this is okay! as it is very// similar to how preferred-activities are treatedreadPersistentPreferredActivitiesLPw(parser, 0);} else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {// TODO: check whether this is okay! as it is very// similar to how preferred-activities are treatedreadCrossProfileIntentFiltersLPw(parser, 0);} else if (tagName.equals(TAG_DEFAULT_BROWSER)) {readDefaultAppsLPw(parser, 0);} else if (tagName.equals("updated-package")) {// 注意这里,updated-package记录的package视为disabledreadDisabledSysPackageLPw(parser);} else if (tagName.equals("renamed-package")) {String nname = parser.getAttributeValue(null, "new");String oname = parser.getAttributeValue(null, "old");if (nname != null && oname != null) {mRenamedPackages.put(nname, oname);}} else if (tagName.equals("restored-ivi")) {readRestoredIntentFilterVerifications(parser);} else if (tagName.equals("last-platform-version")) {// Upgrade from older XML schemafinal VersionInfo internal = findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);final VersionInfo external = findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);internal.sdkVersion = XmlUtils.readIntAttribute(parser, "internal", 0);external.sdkVersion = XmlUtils.readIntAttribute(parser, "external", 0);internal.fingerprint = external.fingerprint =XmlUtils.readStringAttribute(parser, "fingerprint");} else if (tagName.equals("database-version")) {// Upgrade from older XML schemafinal VersionInfo internal = findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);final VersionInfo external = findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);internal.databaseVersion = XmlUtils.readIntAttribute(parser, "internal", 0);external.databaseVersion = XmlUtils.readIntAttribute(parser, "external", 0);} else if (tagName.equals("verifier")) {final String deviceIdentity = parser.getAttributeValue(null, "device");try {mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity);} catch (IllegalArgumentException e) {Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: "+ e.getMessage());}} else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) {final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT);mReadExternalStorageEnforced ="1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE;} else if (tagName.equals("keyset-settings")) {mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);} else if (TAG_VERSION.equals(tagName)) {final String volumeUuid = XmlUtils.readStringAttribute(parser,ATTR_VOLUME_UUID);final VersionInfo ver = findOrCreateVersion(volumeUuid);ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_DATABASE_VERSION);ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);} else {Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "+ parser.getName());XmlUtils.skipCurrentTag(parser);}}str.close();} catch (XmlPullParserException e) {...} catch (java.io.IOException e) {...}...return true;
}

3、Packages.xml的作用

在上篇文章中我们可以看到,packages.xml文件最终被解析和保存到了Settings的mPackages属性里了。来看一下PMS的构造方法里,它都发挥了什么作用吧

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {...mSettings = injector.getSettings();...t.traceBegin("addSharedUsers");// 创建一些列系统shared user idmSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);...// CHECKSTYLE:OFF IndentationChecksynchronized (mInstallLock) {// writersynchronized (mLock) {...// 读取packages.xml或packages-backup.xml,两者的格式一样,上次更新packages.xml出现异常的时候才会出现packages-backup.xmlt.traceBegin("read user settings");mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false));t.traceEnd();// 清除代码路径不存在的packagefinal int packageSettingCount = mSettings.mPackages.size();for (int i = packageSettingCount - 1; i >= 0; i--) {PackageSetting ps = mSettings.mPackages.valueAt(i);if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {mSettings.mPackages.removeAt(i);mSettings.enableSystemPackageLPw(ps.name);}}if (!mOnlyCore && mFirstBoot) {requestCopyPreoptedFiles();}...// Save the names of pre-existing packages prior to scanning, so we can determine// which system packages are completely new due to an upgrade.// 在扫描之前保存预先存在的App的名称,以便确定哪些系统App由于升级完全新加的if (isDeviceUpgrading()) {mExistingPackages = new ArraySet<>(mSettings.mPackages.size());for (PackageSetting ps : mSettings.mPackages.values()) {mExistingPackages.add(ps.name);}}// 扫描系统App,这里代码省略...// Prune any system packages that no longer exist.final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();// Stub packages must either be replaced with full versions in the /data// partition or be disabled.final List<String> stubSystemApps = new ArrayList<>();if (!mOnlyCore) {...final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();while (psit.hasNext()) {PackageSetting ps = psit.next();// 非系统App跳过if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {continue;}final AndroidPackage scannedPkg = mPackages.get(ps.name);if (scannedPkg != null) { // packages.xml和即时扫描的结果都存在这个package// 如果系统App扫描到了,而且是disabled状态,也就是被记录在updated-package标签里,把这个记录删除,以期使用用户安装的版本// 后面如果没有找到用户安装的版本,会恢复系统自带的版本if (mSettings.isDisabledSystemPackageLPr(ps.name)) {logCriticalInfo(Log.WARN,"Expecting better updated system app for " + ps.name+ "; removing system app.  Last known"+ " codePath=" + ps.codePathString+ ", versionCode=" + ps.versionCode+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());removePackageLI(scannedPkg, true);mExpectingBetter.put(ps.name, ps.codePath);}continue;}// packages.xml中存在的package如果没有被扫描到执行接下来的代码if (!mSettings.isDisabledSystemPackageLPr(ps.name)) { // 不是disabled状态说明用户没有手动更新过,直接删除psit.remove();logCriticalInfo(Log.WARN, "System package " + ps.name+ " no longer exists; it's data will be wiped");// Assume package is truly gone and wipe residual permissions.mPermissionManager.updatePermissions(ps.name, null);// 真正删除代码和数据的操作会在后面执行} else {// 在disabled list里,判断代码路径是不是还存在,存在的话可能是升级的时候改了包名,不存在则可能删除了final PackageSetting disabledPs =mSettings.getDisabledSystemPkgLPr(ps.name);if (disabledPs.codePath == null || !disabledPs.codePath.exists()|| disabledPs.pkg == null) {possiblyDeletedUpdatedSystemApps.add(ps.name);} else {// 加到mExpectingBetter,以便后续扫描到对应的升级版本的时候继续保持系统版本disabled,而使用用户版本,没有扫描到则再处理是删除还是保留mExpectingBetter.put(disabledPs.name, disabledPs.codePath);}}}}final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();// 移除那些没有package关联的shared user idmSettings.pruneSharedUsersLPw();...// 扫描用户安装的Appif (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,packageParser, executorService);}packageParser.close();List<Runnable> unfinishedTasks = executorService.shutdownNow();if (!unfinishedTasks.isEmpty()) {throw new IllegalStateException("Not all tasks finished before calling close: "+ unfinishedTasks);}if (!mOnlyCore) {// Remove disable package settings for updated system apps that were// removed via an OTA. If the update is no longer present, remove the// app completely. Otherwise, revoke their system privileges.// 系统升级中移除了App,如果App还存在于用户区(用户手动安装过新版本),剥夺App的系统级权限,否则完全删除for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {final String packageName = possiblyDeletedUpdatedSystemApps.get(i);final AndroidPackage pkg = mPackages.get(packageName);final String msg;// remove from the disabled system list; do this first so any future// scans of this package are performed without this statemSettings.removeDisabledSystemPackageLPw(packageName);if (pkg == null) {// 这里仍然没找到扫描结果,直接删除msg = "Updated system package " + packageName+ " no longer exists; removing its data";// 真正删除代码和数据的操作会在后面执行} else {// 扫描到了,剥夺系统级权限msg = "Updated system package " + packageName+ " no longer exists; rescanning package on data";// NOTE: We don't do anything special if a stub is removed from the// system image. But, if we were [like removing the uncompressed// version from the /data partition], this is where it'd be done.// remove the package from the system and re-scan it without any// special privileges// 先删除,后重新扫描removePackageLI(pkg, true);try {final File codePath = new File(pkg.getCodePath());// 重新扫描scanPackageTracedLI(codePath, 0, scanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse updated, ex-system package: "+ e.getMessage());}}// 最终确认结果final PackageSetting ps = mSettings.mPackages.get(packageName);if (ps != null && mPackages.get(packageName) == null) {removePackageDataLIF(ps, null, null, 0, false);}logCriticalInfo(Log.WARN, msg);}/** Make sure all system apps that we expected to appear on* the userdata partition actually showed up. If they never* appeared, crawl back and revive the system version.*/// 确保应该在用户区出现的系统App存在,不存在则使用系统区的版本for (int i = 0; i < mExpectingBetter.size(); i++) {final String packageName = mExpectingBetter.keyAt(i);if (!mPackages.containsKey(packageName)) {final File scanFile = mExpectingBetter.valueAt(i);logCriticalInfo(Log.WARN, "Expected better " + packageName+ " but never showed up; reverting to system");@ParseFlags int reparseFlags = 0;@ScanFlags int rescanFlags = 0;for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {final ScanPartition partition = mDirsToScanAsSystem.get(i1);if (partition.containsPrivApp(scanFile)) {reparseFlags = systemParseFlags;rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED| partition.scanFlag;break;}if (partition.containsApp(scanFile)) {reparseFlags = systemParseFlags;rescanFlags = systemScanFlags | partition.scanFlag;break;}}if (rescanFlags == 0) {Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);continue;}mSettings.enableSystemPackageLPw(packageName);try {scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);} catch (PackageManagerException e) {Slog.e(TAG, "Failed to parse original system package: "+ e.getMessage());}}}...}mExpectingBetter.clear();...for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {// NOTE: We ignore potential failures here during a system scan (like// the rest of the commands above) because there's precious little we// can do about it. A settings error is reported, though.final List<String> changedAbiCodePath =applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/,mInjector.getAbiHelper().getAdjustedAbiForSharedUser(setting.packages, null /*scannedPackage*/));if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {final String codePathString = changedAbiCodePath.get(i);try {mInstaller.rmdex(codePathString,getDexCodeInstructionSet(getPreferredInstructionSet()));} catch (InstallerException ignored) {}}}// Adjust seInfo to ensure apps which share a sharedUserId are placed in the same// SELinux domain.setting.fixSeInfoLocked();setting.updateProcesses();}// Now that we know all the packages we are keeping,// read and update their last usage times.mPackageUsage.read(mSettings.mPackages);...t.traceBegin("write settings");mSettings.writeLPr();...mSettings.setPermissionControllerVersion(getPackageInfo(mRequiredPermissionControllerPackage, 0,UserHandle.USER_SYSTEM).getLongVersionCode());...} // synchronized (mLock)} // synchronized (mInstallLock)...
}

这里删除了非常多的代码,只列出了关键性的。可以看到packages.xml的主要作用存储上一次启动时扫描和更新的结果,和本次启动扫描的结果进行比较,判断哪些该更新,哪些该删除。这就是它的主要作用。

这篇关于Android 11 PackageManagerService源码分析(二):Packages.xml详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105635

相关文章

MySQL中的分组和多表连接详解

《MySQL中的分组和多表连接详解》:本文主要介绍MySQL中的分组和多表连接的相关操作,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录mysql中的分组和多表连接一、MySQL的分组(group javascriptby )二、多表连接(表连接会产生大量的数据垃圾)MySQL中的

Java 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

SpringBoot3.4配置校验新特性的用法详解

《SpringBoot3.4配置校验新特性的用法详解》SpringBoot3.4对配置校验支持进行了全面升级,这篇文章为大家详细介绍了一下它们的具体使用,文中的示例代码讲解详细,感兴趣的小伙伴可以参考... 目录基本用法示例定义配置类配置 application.yml注入使用嵌套对象与集合元素深度校验开发

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

《SpringBoot整合mybatisPlus实现批量插入并获取ID详解》这篇文章主要为大家详细介绍了SpringBoot如何整合mybatisPlus实现批量插入并获取ID,文中的示例代码讲解详细... 目录【1】saveBATch(一万条数据总耗时:2478ms)【2】集合方式foreach(一万条数

Python装饰器之类装饰器详解

《Python装饰器之类装饰器详解》本文将详细介绍Python中类装饰器的概念、使用方法以及应用场景,并通过一个综合详细的例子展示如何使用类装饰器,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. 引言2. 装饰器的基本概念2.1. 函数装饰器复习2.2 类装饰器的定义和使用3. 类装饰

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

MySQL 中的 JSON 查询案例详解

《MySQL中的JSON查询案例详解》:本文主要介绍MySQL的JSON查询的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 的 jsON 路径格式基本结构路径组件详解特殊语法元素实际示例简单路径复杂路径简写操作符注意MySQL 的 J