1、開篇
在上一篇文章中提到Settings類會在PackageManagerService啟動過程中對packages.xml等一些列xml檔案進行解析。那麼有以下問題:
- 這些檔案記錄了什麼内容?
- 為什麼需要這些檔案?
讓我們一起通過閱讀源碼解決這些問題吧。
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主要記錄了以下幾方面的資訊:
- 權限資訊,permission-trees标簽和permissions标簽。這裡記錄的是系統裡所有的權限條目
-
安裝的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中聲明的權限而不是已經授予的權限。
- 共享使用者資訊,shared-user标簽。一般來說一個Android系統會為每一個App配置設定一個user id,但是我們也可以在manifest元素中指定android:sharedUserId屬性,使多個App具有同樣的user id進而可以共享資料等等。但是除了系統App,Android強烈建議我們不要使用它,并且在未來的版本可能會被移除,是以了解即可。
- 簽名資訊,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 values
findOrCreateVersion(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 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")) {
// 注意這裡,updated-package記錄的package視為disabled
readDisabledSysPackageLPw(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 schema
final 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 schema
final 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 id
mSettings.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 IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
// 讀取packages.xml或packages-backup.xml,兩者的格式一樣,上次更新packages.xml出現異常的時候才會出現packages-backup.xml
t.traceBegin("read user settings");
mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(false));
t.traceEnd();
// 清除代碼路徑不存在的package
final 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 id
mSettings.pruneSharedUsersLPw();
...
// 掃描使用者安裝的App
if (!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 state
mSettings.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的主要作用存儲上一次啟動時掃描和更新的結果,和本次啟動掃描的結果進行比較,判斷哪些該更新,哪些該删除。這就是它的主要作用。