天天看點

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 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的主要作用存儲上一次啟動時掃描和更新的結果,和本次啟動掃描的結果進行比較,判斷哪些該更新,哪些該删除。這就是它的主要作用。

繼續閱讀