文章目录
前言
上一章我们以PackageManagerService装载结束为切入点,通过dump package结果分析了PMS部分类、以及类之间的关联关系。本章我们正向分析,以开机为切入点,带着结果,探索相关对象的装载流程。
注:笔者撰写本文的软件环境为android 13
一、PackageManagerService初始化
PackageManagerService服务是在SystemServer中实例化,PackageManagerService涉及到的工作流程较多,限于篇幅原因,本文主要分析两部分:1.读取应用历史记录 2. 应用扫描。入口代码如下所示:
//framework/base/service/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(...) {
...
//1.读取应用历史记录
mFirstBoot = !mSettings.readLPw(computer,
mInjector.getUserManagerInternal().getUsers(
/* excludePartial= */ true,
/* excludeDying= */ false,
/* excludePreCreated= */ false));
...
//2.应用扫描
mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
startTime);
mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
packageParser.close();
}
PackageManagerService源码分析一:从dump package源码,探索类之间关联关系一文中我们分析了PMS部分类的关联关系,本文通过如下两个流程,探索相关类的装载流程。
- 应用状态读取
- 应用扫描
二、readLPw
readLpw部分代码如下
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
//packages-backup.xml为packages.xml的备份文件
//xml写入数据的时候为了防止写入失败(掉电等)
if (mBackupSettingsFilename.exists()) {
try {
str = new FileInputStream(mBackupSettingsFilename);
...
} catch (java.io.IOException e) {
...
}
}
try {
if (str == null) {
if (!mSettingsFilename.exists()) {
...
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) {
...
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;
}
...
}
} catch (XmlPullParserException e) {
...
}
//packages-stopped.xml为老版本上的文件,为了系统向下兼容,需要将mStoppedPackagesFilename数据迁移
if (mBackupStoppedPackagesFilename.exists()
|| mStoppedPackagesFilename.exists()) {
// Read old file
readStoppedLPw();
mBackupStoppedPackagesFilename.delete();
mStoppedPackagesFilename.delete();
// Migrate to new file format
writePackageRestrictionsLPr(UserHandle.USER_SYSTEM);
} else {
for (UserInfo user : users) {
//读取用户的package-restrictions.xml
readPackageRestrictionsLPr(user.id);
}
}
for (UserInfo user : users) {
//读取应用权限
mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
}
结合readLPw相关代码,可以看到流程主要读取了如下几个文件:
- packages.xml:故名思义,记录包应用代码位置、权限、签名等信息
- packages.list:包信息列表,记录部分包信息
- packages-stopped.xml:记录被停用的包信息。新系统平台迁移,将信息转化为package-restrictions.xml存储
- package-restrictions.xml:记录被停用、偏好设置等用户行为产生应用包信息,常见的用户行为影响的应用为开机引导:开机引导流程走完,需要将应用设置为不可用,只有恢复出厂设置才能重新进入开机引导。
- runtime-permissions.xml:记录包应用权限信息
本小节主要分析 packages.xml、package-restrictions.xml、runtime-permissions.xml文件的解析。解析文件的部分时序图流程如下:
1. 解析 packages.xml
packages.xml文件部分内容如下所示,记录包应用代码位置、权限、签名等信息。
<packages>
...
<permissions>
<item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
...
</permissions>
...
</package>
<package name="com.blan.audiotest" codePath="/data/app/~~9WZY4gbCfZm0_Ig8mDxsPg==/com.blan.audiotest-9VKlHs9xCrkt3gttbPby5w==" nativeLibraryPath="/data/app/~~9WZY4gbCfZm0_Ig8mDxsPg==/com.blan.audiotest-9VKlHs9xCrkt3gttbPby5w==/lib" publicFlags="810073926" privateFlags="-1946152704" ft="18fad8c6390" ut="18fad8c67ee" version="1" userId="10134" packageSource="0" loadingProgress="1.0" domainSetId="733b2979-7f7d-44a8-82ec-34be8fb26899">
...
</package>
...
<shared-user name="android.uid.system" userId="1000">
<sigs count="1" schemeVersion="3">
<cert index="3" />
</sigs>
</shared-user>
...
<domain-verifications>
<active>
<package-state packageName="com.android.networkstack.tethering" id="91fcd1a1-f52a-48ca-bb57-78a98cd83d40" />
...
</active>
</domain-verifications>
<domain-verifications-legacy>
<user-states packageName="com.android.providers.telephony">
<user-state userId="0" state="0" />
<user-state userId="10" state="0" />
</user-states>
...
</domain-verifications-legacy>
...
</packages>
该文件存储在系统data目录下,下一小节我们介绍package.xml文件的创建。
1.1 Package.xml文件实例
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(File dataDir, PermissionSettings permission,
Object lock) {
...
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
...
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
}
其中,dataDir的路径为Environment.getDataDirectory()为data目录,结合相关代码可知packages.xml和packages-backup.xml文件存储在/data/system目录中。packages-backup.xml可以理解为packages.xml的备份文件。下一小节介绍packages-backup.xml备份文件的创建。
1.2 packages-backup.xml文件创建
packages-backup.xml文件是在更新packages.xml文件时创建,创建方法如下所示:
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
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();
Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
}
}
...
mBackupSettingsFilename.delete();
...
}
可知packages-backup.xml备份文件是通过改名方式实现的,该文件在更新packages.xml前创建、更新时删除。文件备份策略是为了处理紧急下电、异常重启等场景下packages.xml更新异常引入的机制。下一节我们介绍文件的解析。
1.3 文件解析
1.3.1 packages-backup.xml存在
异常下电、重启开机后备份文件packages-backup.xml存在,开机后会优先解析该文件。
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
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()) {
// If both the backup and settings file exist, we
// ignore the settings since it might have been
// corrupted.
Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
+ mSettingsFilename);
mSettingsFilename.delete();
}
} catch (java.io.IOException e) {
// We'll try for the normal settings file.
}
}
}
1.3.2 packages.xml不存在
如果packages-backup.xml文件不存在,流程进入packages.xml文件解析,代码如下所示:
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
...
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 values
findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
return false;
}
str = new FileInputStream(mSettingsFilename);
}
...
}
如果mSettingsFilename.exists()为false,即packages.xml文件不存在,则PackageManagerService会认为这是第一次开机,直接返回,不会进入后续其他文件解析流程。apk解析完成后,会创建packages.xml文件。
1.3.3 解析文件packages.xml
packages-backup.xml为packages.xml文件的备份文件,解析packages-backup.xml与packages.xml逻辑是一致的,部分代码如下所示:
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
...
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")) {
// no longer used.
} 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 treated
readPersistentPreferredActivitiesLPw(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 treated
readCrossProfileIntentFiltersLPw(parser, 0);
} else if (tagName.equals(TAG_DEFAULT_BROWSER)) {
readDefaultAppsLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
}
...
}
...
}
解析packages.xml会实例化对象,部分对象在PackageManagerService源码分析一:从dump package源码,探索类之间关联关系一文中有提及。本小节介绍部分对象的创建。
1.3.3.1 readPackageLPw
解析package标签头,流程进入到readPackageLPw,创建PackageSetting。readPackageLPw部分代码如下:
private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
...
try {
// xml解析
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
idStr = parser.getAttributeValue(null, "userId");
uidError = parser.getAttributeValue(null, "uidError");
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
if (name == null) {
...
} else if (codePathStr == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <package> has no codePath at "
+ parser.getPositionDescription());
} else if (userId > 0) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
pkgPrivateFlags, null /*usesStaticLibraries*/,
null /*usesStaticLibraryVersions*/, null /*mimeGroups*/);
if (packageSetting == null) {
PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
+ userId + " while parsing settings at "
+ parser.getPositionDescription());
} else {
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
}
} else if (sharedIdStr != null) {
if (sharedUserId > 0) {
packageSetting = new PackageSetting(name.intern(), realName, new File(
codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
null /*usesStaticLibraries*/,
null /*usesStaticLibraryVersions*/,
null /*mimeGroups*/);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
+ ": sharedUserId=" + sharedUserId + " pkg=" + packageSetting);
} else {
...
}
} else {
...
}
} catch (NumberFormatException e) {
...
}
...
}
流程中,根据解析信息不同,创建、存储PackageSetting分如下两种场景,
- userId > 0
创建PackageSetting,将创建的对象添加到mPackages中。 - sharedIdStr != null
创建PackageSetting,将创建的对象添加到mPendingPackages中。packages.xml解析完成后,遍历mPendingPackages。创建PackageSettings与SharedUserSetting依赖关系,将创建的对象添加到mPackages中。
1.3.3.2 readSharedUserLPw
解析shared-user标签头,流程进入到readSharedUserLPw,创建SharedUserSetting。
1.3.3.3 readDisabledSysPackageLPw
解析updated-package标签头,流程进入到readDisabledSysPackageLPw,创建PackageSetting,并将对象添加到mDisabledSysPackages中。
1.3.3.4 readPermissions
解析permissions标签头,流程进入到readPermissions,创建BasePermission,并将对象添加到mPermissions中。
2. 解析 package-restrictions.xml
packages.xml文件解析完成后,流程进入到package-restrictions.xml文件解析。android低版本系统中相关信息是存储在packages-stopped.xml,为了系统向下兼容,android高版本系统需要解析packages-stopped.xml,并将解析后的信息记录到package-restrictions.xml文件中存储。
2.1 解析packages-stopped.xml
packages-stopped.xml及其备份文件创建代码如下:
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(File dataDir, PermissionSettings permission,Object lock) {
...
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
...
}
从文件创建代码可知:
- packages-stopped.xml、packages-stopped-backup.xml存储在/data/system目录中;
- 文件不根据用户做区分,即不支持多用户。
packages-stopped.xml及其备份文件的解析比较简单,不进行深入分析。
2.2 解析package-restrictions.xml
如果packages-stopped.xml及packages-stopped-backup.xml文件不存在,流程进入到package-restrictions.xml及其备份文件的解析。
//frameworks/base/services/core/java/com/android/server/pm/Settings.java
private File getUserPackagesStateFile(int userId) {
File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
return new File(userDir, "package-restrictions.xml");
}
android支持多用户,PMS针对单个用户维护单独一个package-restrictions.xml文件。package-restrictions.xml文件部分内容如下。
<package-restrictions>
...
<pkg name="com.android.systemui" first-install-time="18fb2a6f020">
<disabled-components>
<item name="com.android.systemui.saicmotor.MainActivity" />
</disabled-components>
</pkg>
<pkg name="com.saicmotor.hmi.setupwizard" ceDataInode="524853">
<disabled-components>
<item name="com.saicmotor.hmi.setupwizard.activity.WelcomeActivity" />
</disabled-components>
</pkg>
<preferred-activities>
...
<item name="com.android.car.dialer/.ui.TelecomActivity" match="100000" always="true" set="2">
<set name="com.android.car.dialer/.ui.TelecomActivity" />
<set name="com.google.android.car.kitchensink/.KitchenSinkActivity" />
<filter>
<action name="android.intent.action.DIAL" />
<cat name="android.intent.category.DEFAULT" />
</filter>
</item>
...
</preferred-activities>
<default-apps />
</package-restrictions>
package-restrictions.xml有pkg、preferred-activities、default-apps等标签,本小节间要介绍pkg解析流程图。针对单个文件的解析入口为readPackageRestrictionsLPr方法,流程图如下:
针对单个用户部分行为,PMS实例化并记录在PackageUserStateImpl对象中。
方法4:解析禁用组件内容,通过该方法记录在PackageUserStateImpl中。调用pm命令禁止某个组件不可见,package-restrictions.xml体现如下:
<pkg name="com.saicmotor.hmi.setupwizard" ceDataInode="524853">
<disabled-components>
<item name="com.saicmotor.hmi.setupwizard.activity.WelcomeActivity" />
</disabled-components>
</pkg>
方法5与之相反,这里不做赘述。
方法6:解析组件禁用包名调用者,通过该方法记录在PackageUserStateImpl中。使用pm命令禁止某个应用不可用,package-restrictions.xml文件中体现如下:
<pkg name="com.blan.audiotest" enabled="2" enabledCaller="shell:1000" first-install-time="18fae54e63d" />
3. 解析 runtime-permissions.xml
package-restrictions.xml文件解析完成后,流程进入到runtime-permissions.xml文件解析。入口方法为readStateForUserSync,流程图如下
该流程可以分为如下两个子流程。
3.1 解析runtime-permissions.xml
解析runtime-permissions.xml文件的入口方法为parseXml。runtime-permissions.xml存储在data/misc_de/*/apexdata/com.android.permission目录下,其中*代表用户identifier。runtime-permissions.xml文件部分内容如下:
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<runtime-permissions version="8" fingerprint="autochips/full_ac8x_saic_zp/ac8x_saic_zp:11/RQ2A.210505.003/74:userdebug/test-keys?pc_version=300000000">
...
<package name="com.saicmotor.hmi.chargesettings" />
...
<package name="com.blan.cameratest">
<permission name="android.permission.CAMERA" granted="true" flags="301" />
</package>
<package name="com.saicmotor.projection" />
<shared-user name="android.media">
<permission name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
</shared-user>
...
<shared-user name="com.huawei.hms.kitframework.uid">
<permission name="android.permission.READ_PHONE_STATE" granted="true" flags="b0" />
</shared-user>
<shared-user name="android.uid.se" />
...