Android 9.x PMS初始化過程(1)

代碼基于MTK Android 9.x

目錄結(jié)構(gòu)

  1 以上過程的主要調(diào)用序列圖如下:
   2 mOnlyCore
   3 SystemServer.startBootstrapServices
   4 PackageManagerService.main
   5 new PackageManagerService
       5.1 簡(jiǎn)單初始化工作
       5.2 addSharedUserLPw
           5.2.1 uid和權(quán)限的關(guān)系
       5.3 初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件
       5.4 SystemConfig讀取配置文件
           5.4.1 feature配置文件
           5.4.2 系統(tǒng)庫文件
           5.4.3 權(quán)限和gid映射文件
           5.4.4 assign-permission
           5.4.5 allow-in-power-save
       5.5 創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理
       5.6 readLPw
           5.6.1 判斷文件是否存在
           5.6.2 解析packages.xml文件
               5.6.2.1 package字段解析
               5.6.2.2 permissions字段解析
               5.6.2.3 shared-user字段解析
               5.6.2.4 preferred-activities
               5.6.2.5 updated-package字段解析
               5.6.2.6 keyset-settings
               5.6.2.7 mPendingPackages數(shù)據(jù)的處理
           5.6.3 writeKernelMappingLPr
       5.7 驗(yàn)證系統(tǒng)指紋
   6 總結(jié)

以上過程的主要調(diào)用序列圖如下:

PMS初始化流程.png

mOnlyCore

        // Only run "core" apps if we're encrypting the device.
        String cryptState = SystemProperties.get("vold.decrypt");
        if (ENCRYPTING_STATE.equals(cryptState)) {
            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
            mOnlyCore = true;
        } else if (ENCRYPTED_STATE.equals(cryptState)) {
            Slog.w(TAG, "Device encrypted - only parsing core apps");
            mOnlyCore = true;
        }

只有當(dāng)vold.decrypt屬性的值為"trigger_restart_min_framework"和“1”時(shí),mOnlyCore才為true

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    coreApp="true"
    package="com.qiku.xt_security">

mOnlyCore為true表示,PMS只會(huì)對(duì)聲明了 coreApp="true"的應(yīng)用進(jìn)行解析

SystemServer.startBootstrapServices

mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

PackageManagerService.main

    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }
  • factoryTest:表示是否運(yùn)行在工廠模式

  • onlyCore:表示是否運(yùn)行在只解析core應(yīng)用的模式下,也就是加密模式下

  • checkProperties檢查以pm.dexopt開頭的以下屬性的值

    public static final String REASON_STRINGS[] = {
         "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
    };
    
    λ adb shell getprop | grep pm.dexopt
    [pm.dexopt.first-boot]: [quicken]
    [pm.dexopt.boot]: [verify]
    [pm.dexopt.install]: [speed-profile]
    [pm.dexopt.bg-dexopt]: [speed-profile]
    [pm.dexopt.ab-ota]: [speed-profile]
    [pm.dexopt.inactive]: [verify]
    [pm.dexopt.shared]: [speed]
    

然后,初始化PackageManagerService對(duì)象,并添加到ServiceManager中去

new PackageManagerService

簡(jiǎn)單初始化工作

        LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
      
        mContext = context;


        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
        mMetrics = new DisplayMetrics();
        mInstaller = installer;

        // Create sub-components that provide services / data. Order here is important.
        synchronized (mInstallLock) {
        synchronized (mPackages) {
            // Expose private service for system components to use.
            LocalServices.addService(
                    PackageManagerInternal.class, new PackageManagerInternalImpl());
            sUserManager = new UserManagerService(context, this,
                    new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
            mPermissionManager = PermissionManagerService.create(context,
                    new DefaultPermissionGrantedCallback() {
                        @Override
                        public void onDefaultRuntimePermissionsGranted(int userId) {
                            synchronized(mPackages) {
                                mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
                            }
                        }
                    }, mPackages /*externalLock*/);
            mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
            mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);
        }
        }
  • 主要完成變量的賦值;初始化UserManagerService的,UserManagerService用于管理android的多用戶;初始化PermissionManagerService,用于管理應(yīng)用的權(quán)限

  • 初始化mDefaultPermissionPolicy,用于管理平臺(tái)組件默認(rèn)獲取運(yùn)行時(shí)權(quán)限。例如the shell UID is a part of the system and the Phone app should have phone related permission by default.

addSharedUserLPw

       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);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

addSharedUserLPw主要用于初始化一個(gè)SharedUserSetting數(shù)據(jù)結(jié)構(gòu),用于保存聲明了同一個(gè)android:sharedUserId="android.uid.system" 的應(yīng)用信息

    final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<String, SharedUserSetting>();
SharedUserSetting.png

上圖中的userId,指的是uid

一個(gè)應(yīng)用聲明了與其他應(yīng)用共享uid,主要有以下幾個(gè)作用:

  • 兩個(gè)或多個(gè)聲明了同一種sharedUserIds的APK可共享彼此的數(shù)據(jù),并且可運(yùn)行在同一進(jìn)程中

  • 更重要的是,通過聲明特定的sharedUserId,該APK所在進(jìn)程將被賦予指定的UID; SystemUI聲明了system的uid,運(yùn)行SystemUI的進(jìn)程就可享有system用戶所對(duì)應(yīng)的權(quán)限(實(shí)際上就是將該進(jìn)程的uid設(shè)置為system的uid)了

  • AndroidManifest.xml中聲明sharedUserId外,APK在編譯時(shí)還必須使用對(duì)應(yīng)的證書進(jìn)行簽名。例如SystemUI,在其Android.mk中需要額外聲明LOCAL_CERTIFICATE := platform,如此,才可獲得指定的UID

    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
        SharedUserSetting s = mSharedUsers.get(name);
        if (s != null) {
            if (s.userId == uid) {
                return s;
            }
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared user, keeping first: " + name);
            return null;
        }
        s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }


    private boolean addUserIdLPw(int uid, Object obj, Object name) {
        if (uid > Process.LAST_APPLICATION_UID) {
            return false;
        }

        if (uid >= Process.FIRST_APPLICATION_UID) {//普通應(yīng)用,其值大于10000
            int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) {
                mUserIds.add(null);
                N++;
            }
            if (mUserIds.get(index) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate user id: " + uid
                        + " name=" + name);
                return false;
            }
            mUserIds.set(index, obj);
        } else {//系統(tǒng)應(yīng)用,其值小于10000
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared id: " + uid
                                + " name=" + name);
                return false;
            }
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }

除了創(chuàng)建一個(gè)map外,settings中還會(huì)創(chuàng)建另外兩個(gè)數(shù)據(jù)結(jié)構(gòu),mUserIds和mOtherUserIds,這兩個(gè)數(shù)據(jù)結(jié)構(gòu),通過數(shù)組下表映射進(jìn)行查詢,速度比mSharedUsers更快
mUserIds是一個(gè)ArrayList,存儲(chǔ)普通應(yīng)用,以(uid-Process.FIRST_APPLICATION_UID)為index,SharedUserSetting為value
mOtherUserIds是一個(gè),SparseArray,存儲(chǔ)系統(tǒng)應(yīng)用,以u(píng)id為index,SharedUserSetting為value

uid和權(quán)限的關(guān)系

之所以不同的應(yīng)用,通過指定相同的uid,可以共享權(quán)限,是因?yàn)?,系統(tǒng)中是通過uid為映射,存儲(chǔ)應(yīng)用的權(quán)限數(shù)據(jù)的。不同的應(yīng)用,具有同一個(gè)uid,查到的權(quán)限數(shù)據(jù)是相同的

初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件

        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                "*dexopt*");
        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
                installer, mInstallLock);
        mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock,
                dexManagerListener);
        mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

        mOnPermissionChangeListeners = new OnPermissionChangeListeners(
                FgThread.get().getLooper());

        getDefaultDisplayMetrics(context, mMetrics);

初始化dex優(yōu)化相關(guān)類

SystemConfig讀取配置文件

 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
 SystemConfig systemConfig = SystemConfig.getInstance();
 mAvailableFeatures = systemConfig.getAvailableFeatures();
 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

 mProtectedPackages = new ProtectedPackages(mContext);

通過SystemConfig,解析各個(gè)目錄下/etc/permissions和/etc/sysconfig下的各個(gè)配置文件

    SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
      ....
    }

讀取的文件目錄的順序?yàn)?system-->/vendor-->/odm-->/oem-->/product

以permissions目錄下的文件為例:

full_k61v1_64_bsp:/system/etc/permissions # ls
android.hardware.faketouch.xml
android.hardware.location.gps.xml
android.hardware.microphone.xml
android.hardware.touchscreen.multitouch.distinct.xml
android.hardware.touchscreen.multitouch.jazzhand.xml
android.hardware.touchscreen.multitouch.xml
android.hardware.touchscreen.xml
android.hardware.usb.host.xml
android.software.live_wallpaper.xml
android.software.webview.xml
com.android.location.provider.xml
com.android.media.remotedisplay.xml
com.android.mediadrm.signer.xml
com.mediatek.ims.plugin.xml
org.simalliance.openmobileapi.xml
platform.xml
pms_sysapp_removable_system_list.txt
privapp-permissions-mediatek.xml
privapp-permissions-platform.xml

feature配置文件

android.hardware..和android.hardware..文件中中聲明了手機(jī)能夠支持的的硬件特性,例如支持camera、支持藍(lán)牙等

數(shù)據(jù)格式為:

<permissions>
    <feature name="android.software.webview" />
</permissions>

解析后的數(shù)據(jù)存儲(chǔ)在SystemConfig.mAvailableFeatures中

   final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
    private void addFeature(String name, int version) {
        FeatureInfo fi = mAvailableFeatures.get(name);
        if (fi == null) {
            fi = new FeatureInfo();
            fi.name = name;
            fi.version = version;
            mAvailableFeatures.put(name, fi);
        } else {
            fi.version = Math.max(fi.version, version);
        }
    }

可以通過adb shell pm list feature查看手機(jī)支持的feature

full_k61v1_64_bsp:/ # pm list features
feature:reqGlEsVersion=0x30002
feature:android.hardware.audio.low_latency
feature:android.hardware.audio.output
feature:android.hardware.bluetooth
feature:android.hardware.bluetooth_le
feature:android.hardware.camera
feature:android.hardware.camera.any
feature:android.hardware.camera.autofocus

系統(tǒng)庫文件

library聲明的是系統(tǒng)提供的Java庫,應(yīng)用程序運(yùn)行時(shí)候必須要鏈接這些庫,該工作由系統(tǒng)自動(dòng)完成

數(shù)據(jù)格式為:

<permissions>
    <library name="com.android.media.remotedisplay"
            file="/system/framework/com.android.media.remotedisplay.jar" />
</permissions>

數(shù)據(jù)解析后,存儲(chǔ)在mSharedLibraries中

    final ArrayMap<String, String> mSharedLibraries  = new ArrayMap<>();
    ....
    else if ("library".equals(name) && allowLibs) {
        String lname = parser.getAttributeValue(null, "name");
        String lfile = parser.getAttributeValue(null, "file");
        if (lname == null) {
              Slog.w(TAG, "<library> without name in " + permFile + " at "
                                + parser.getPositionDescription());
         } else if (lfile == null) {
              Slog.w(TAG, "<library> without file in " + permFile + " at "
                                + parser.getPositionDescription());
         } else {
              //Log.i(TAG, "Got library " + lname + " in " + lfile);
              mSharedLibraries.put(lname, lfile);
         }

可以通過pm list libraries命令,查看系統(tǒng)中支持的系統(tǒng)庫

full_k61v1_64_bsp:/ # pm list libraries
library:android.ext.services
library:android.ext.shared
library:android.test.base
library:android.test.mock
library:android.test.runner
library:com.android.future.usb.accessory
library:com.android.location.provider
library:com.android.media.remotedisplay
library:com.android.mediadrm.signer
library:com.mediatek.ims.plugin
library:javax.obex
library:org.apache.http.legacy
library:org.simalliance.openmobileapi

權(quán)限和gid映射文件

permission和group用于建立Linux層gid和Android層pemission之間的映射關(guān)系

具有android.permission.BLUETOOTH_ADMIN權(quán)限的應(yīng)用,將獲得net_bt_admin組權(quán)限

數(shù)據(jù)格式為:

    <permission name="android.permission.BLUETOOTH_ADMIN" >
        <group gid="net_bt_admin" />
    </permission>

    <permission name="android.permission.BLUETOOTH" >
        <group gid="net_bt" />
    </permission>

    <permission name="android.permission.BLUETOOTH_STACK" >
        <group gid="bluetooth" />
        <group gid="wakelock" />
        <group gid="uhid" />
    </permission>

gid的名稱和數(shù)字的對(duì)應(yīng)是在system\core\include\private\android_filesystem_config.h中定義的

#define AID_NET_BT 3002       /* bluetooth: create sco, rfcomm or l2cap sockets */

解析后,數(shù)據(jù)存儲(chǔ)在mPermissions中,PermissionEntry存儲(chǔ)了改權(quán)限關(guān)聯(lián)的gid

final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();


                } else if ("permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");//解析name字段
                    if (perm == null) {
                        Slog.w(TAG, "<permission> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    readPermission(parser, perm);


    void readPermission(XmlPullParser parser, String name)
            throws IOException, XmlPullParserException {
        if (mPermissions.containsKey(name)) {
            throw new IllegalStateException("Duplicate permission definition for " + name);
        }

        final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
        final PermissionEntry perm = new PermissionEntry(name, perUser);
        mPermissions.put(name, perm);

        int outerDepth = parser.getDepth();
        int type;
        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 ("group".equals(tagName)) {
                String gidStr = parser.getAttributeValue(null, "gid");
                if (gidStr != null) {
                    int gid = Process.getGidForName(gidStr);
                    perm.gids = appendInt(perm.gids, gid);
                } else {
                    Slog.w(TAG, "<group> without gid at "
                            + parser.getPositionDescription());
                }
            }
            XmlUtils.skipCurrentTag(parser);
        }
    }

assign-permission

賦予對(duì)應(yīng)uid相應(yīng)的權(quán)限。如果下面一行表示uid為media,那么就賦予它ACCESS_SURFACE_FLINGER的權(quán)限,其實(shí)就是把它加到對(duì)應(yīng)的用戶組中

數(shù)據(jù)格式為:


<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
<assign-permission name="android.permission.WAKE_LOCK" uid="media" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
<assign-permission name="android.permission.WAKE_LOCK" uid="cameraserver" />

解析后,存放在SystemConfig.mSystemPermissions數(shù)據(jù)結(jié)構(gòu)中

final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
                } else if ("assign-permission".equals(name) && allowPermissions) {
                    String perm = parser.getAttributeValue(null, "name");
                    if (perm == null) {
                        Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    String uidStr = parser.getAttributeValue(null, "uid");
                    if (uidStr == null) {
                        Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    int uid = Process.getUidForName(uidStr);
                    if (uid < 0) {
                        Slog.w(TAG, "<assign-permission> with unknown uid \""
                                + uidStr + "  in " + permFile + " at "
                                + parser.getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                    perm = perm.intern();
                    ArraySet<String> perms = mSystemPermissions.get(uid);
                    if (perms == null) {
                        perms = new ArraySet<String>();
                        mSystemPermissions.put(uid, perms);
                    }
                    perms.add(perm);
                    XmlUtils.skipCurrentTag(parser);

                } 

allow-in-power-save

允許應(yīng)用在省電模式下,訪問網(wǎng)絡(luò)

數(shù)據(jù)格式為:

 <!-- These are the standard packages that are white-listed to always have internet
         access while in power save mode, even if they aren't in the foreground. -->
    <allow-in-power-save package="com.android.providers.downloads" />

    <!-- need by ADUPS_FOTA_SUPPORT or FOTA_UPDATE_SUPPORT -->
    <allow-in-power-save package="com.adups.fota" />
    <allow-in-power-save package="com.adups.fota.sysoper" />

    <!-- These are the standard packages that are white-listed to always have internet
         access while in data mode, even if they aren't in the foreground. -->
    <allow-in-data-usage-save package="com.android.providers.downloads" />

    <!-- This is a core platform component that needs to freely run in the background -->
    <allow-in-power-save package="com.android.cellbroadcastreceiver" />
    <allow-in-power-save package="com.android.shell" />

解析后,存儲(chǔ)在

final ArraySet<String> mAllowInPowerSave = new ArraySet<>();

創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理

            //創(chuàng)建PMS的HanderThread,進(jìn)行異步任務(wù)的處理
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());

            mProcessLoggingHandler = new ProcessLoggingHandler();

            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
            mInstantAppRegistry = new InstantAppRegistry(this);
           
            //獲取上文所說的系統(tǒng)共享庫
            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
            final int builtInLibCount = libConfig.size();
            for (int i = 0; i < builtInLibCount; i++) {
                String name = libConfig.keyAt(i);//共享庫的名字
                String path = libConfig.valueAt(i);//共享庫的路徑
                addSharedLibraryLPw(path, null, name, SharedLibraryInfo.VERSION_UNDEFINED,
                        SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
            }

addSharedLibraryLPw中各個(gè)參數(shù)的意義:

PLATFORM_PACKAGE_NAME : "android"

TYPE_BUILTIN = 0;: Shared library type: this library is a part of the OS and cannot be updated or uninstalled.

VERSION_UNDEFINED = -1: Constant for referring to an undefined version.

如上文所示,共享庫的格式為:

<permissions>
    <library name="com.android.media.remotedisplay"
            file="/system/framework/com.android.media.remotedisplay.jar" />
</permissions>

addSharedLibraryLPw

    private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
            int type, String declaringPackageName, long declaringVersionCode) {
        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
        if (versionedLib == null) {
            versionedLib = new LongSparseArray<>();
            mSharedLibraries.put(name, versionedLib);
            if (type == SharedLibraryInfo.TYPE_STATIC) {//如果是靜態(tài)類型的系統(tǒng)共享包,各個(gè)類型的意義見下面代碼解釋
                mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
            }
        } else if (versionedLib.indexOfKey(version) >= 0) {
            return false;
        }
        SharedLibraryEntry libEntry = new SharedLibraryEntry(path, apk, name,
                version, type, declaringPackageName, declaringVersionCode);
        versionedLib.put(version, libEntry);
        return true;
    }


    /**
     * Shared library type: this library is a part of the OS
     * and cannot be updated or uninstalled.
     */
    public static final int TYPE_BUILTIN = 0;

    /**
     * Shared library type: this library is backwards-compatible, can
     * be updated, and updates can be uninstalled. Clients link against
     * the latest version of the library.
     */
    public static final int TYPE_DYNAMIC = 1;

    /**
     * Shared library type: this library is <strong>not</strong> backwards
     * -compatible, can be updated and updates can be uninstalled. Clients
     * link against a specific version of the library.
     */
    public static final int TYPE_STATIC = 2;

上述代碼邏輯對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為:

final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
( name="com.android.media.remotedisplay" LongSparseArray<SharedLibraryEntry>)
(int version, SharedLibraryEntry)

即可以通過name-->得到該系統(tǒng)共享包對(duì)應(yīng)的各個(gè)版本的SharedLibraryEntry信息

readLPw

LPw, local printwrite

改方法主要對(duì)三個(gè)文件進(jìn)行讀取

  • packages.xml,packages_backup.xml: PKMS掃描完目標(biāo)文件夾后會(huì)創(chuàng)建該文件。
    當(dāng)系統(tǒng)進(jìn)行程序安裝、卸載和更新等操作時(shí),均會(huì)更新該文件。該文件保存了系統(tǒng)中與package相關(guān)的一些信息。
    backup是臨時(shí)文件。PKMS先把數(shù)據(jù)寫到backup中,信息都寫成功后再改名成非backup的文件。
    其目的是防止在寫文件過程中出錯(cuò),導(dǎo)致信息丟失;如果存在backup文件,則表示寫入過程中,被異常中指。

  • packages.list:描述系統(tǒng)中存在的所有非系統(tǒng)自帶的APK的信息。當(dāng)這些程序有變動(dòng)時(shí),PKMS就會(huì)更新該文件。

  • packages-stopped.xml:從系統(tǒng)自帶的設(shè)置程序中進(jìn)入應(yīng)用程序頁面,然后在選擇強(qiáng)制停止(ForceStop)某個(gè)應(yīng)用時(shí),系統(tǒng)會(huì)將該應(yīng)用的相關(guān)信息記錄到此文件中。也就是該文件保存系統(tǒng)中被用戶強(qiáng)制停止的Package的信息。

packages-stopped.xml文件,在Android 9去除了

在該方法開始的時(shí)候,會(huì)判斷packages.xml,packages_backup.xml是否存在,如果都不能存在的話,會(huì)直接返回false

判斷文件是否存在

        Log.d(TAG, Log.getStackTraceString(new Throwable()));
        FileInputStream str = null;
        if (mBackupSettingsFilename.exists()) {
            try {
                //如果packages_backup.xml文件存在,則設(shè)置成,讀取backup文件
                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_backup.xml和packages.xml兩個(gè)文件都存在,則刪除packages.xml文件,因?yàn)?,正常的文件寫入流程可能已?jīng)被中斷
                    // 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.
            }
        }
        //清楚本都數(shù)據(jù)信息,每個(gè)屬性代表的字段,后面講解
        mPendingPackages.clear();
        mPastSignatures.clear();
        mKeySetRefs.clear();
        mInstallerPackages.clear();

        try {
            if (str == null) {
                //如果packages_backup.xml和packages.xml兩個(gè)文件都不存在,則讀取fingerprint屬性值后,退出
                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
                   //獲取系統(tǒng)指紋,此時(shí)packages.xml文件不存在,從系統(tǒng)屬性中獲取系統(tǒng)指紋
                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
                    return false;
                }
                str = new FileInputStream(mSettingsFilename);
            }

獲取系統(tǒng)指紋的方法為:
先判斷ro.build.fingerprint屬性的值,不為空的情況下,通過多個(gè)屬性值進(jìn)行拼接
處于各種目的,有的項(xiàng)目會(huì)將ro.build.fingerprint設(shè)置成固定的值

    private static String deriveFingerprint() {
        String finger = SystemProperties.get("ro.build.fingerprint");
        if (TextUtils.isEmpty(finger)) {
            finger = getString("ro.product.brand") + '/' +
                    getString("ro.product.name") + '/' +
                    getString("ro.product.device") + ':' +
                    getString("ro.build.version.release") + '/' +
                    getString("ro.build.id") + '/' +
                    getString("ro.build.version.incremental") + ':' +
                    getString("ro.build.type") + '/' +
                    getString("ro.build.tags");
        }
        return finger;
    }

解析packages.xml文件

解析和讀取packages.xml文件,如下所示,我們一個(gè)一個(gè)的分析,packages.xml文件的各個(gè)字段的解析過程

 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);
                } else if (tagName.equals("cleaning-package")) {
                    String name = parser.getAttributeValue(null, ATTR_NAME);
                    String userStr = parser.getAttributeValue(null, ATTR_USER);
                    String codeStr = parser.getAttributeValue(null, ATTR_CODE);
                    if (name != null) {
                        int userId = UserHandle.USER_SYSTEM;
                        boolean andCode = true;
                        try {
                            if (userStr != null) {
                                userId = Integer.parseInt(userStr);
                            }
                        } catch (NumberFormatException e) {
                        }
                        if (codeStr != null) {
                            andCode = Boolean.parseBoolean(codeStr);
                        }
                        addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode));
                    }
                } 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);
                    /* 360OS begin, add for fingerprint_runtime */
                    try {
                        ver.fingerprintRuntime = XmlUtils.readStringAttribute(parser,
                                FingerPrintRuntime.ATTR_FINGERPRINT_RUNTIME);
                    } catch (Exception e) {
                        ver.fingerprintRuntime = "";
                        e.printStackTrace();
                    }
                    /* 360OS end */
                } else {
                    Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
                            + parser.getName());
                    XmlUtils.skipCurrentTag(parser);
                }
            }

            str.close();

        } catch (XmlPullParserException e) {
            mReadMessages.append("Error reading: " + e.toString());
            PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
            Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);

        } catch (java.io.IOException e) {
            mReadMessages.append("Error reading: " + e.toString());
            PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
            Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
        }
package字段解析

數(shù)據(jù)格式為:

    <package name="com.android.systemui" codePath="/system/priv-app/QK_SystemUIML" nativeLibraryPath="/system/priv-app/QK_SystemUIML/lib" primaryCpuAbi="arm64-v8a" publicFlags="810040845" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="1" sharedUserId="10035" isOrphaned="true" 
    installer="...">
        <sigs count="1" schemeVersion="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
            <item name="android.permission.REMOTE_AUDIO_PLAYBACK" granted="true" flags="0" />
            <item name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" granted="true" flags="0" />
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.CARE_MODE_CHANGED" granted="true" flags="0" />
            ....
        </perms>
        <proper-signing-keyset identifier="3" />
    </package>

** 各個(gè)字段意義: **

  • name:應(yīng)用包名
  • codePath:應(yīng)用安裝位置,可以安裝在:/system/priv-app/,/system/app/,/vendor/overlay,/system/presetapp,/data/app目錄
  • nativeLibraryPath:表示 app 使用的 xxx.so 庫存放的位置
  • primaryCpuAbi: app 以哪種 abi 架構(gòu)運(yùn)行
  • ft 表示 apk 文件上次被更改的時(shí)間, it 表示 app 第一次安裝的時(shí)間, ut 表示 app 上次被更新時(shí)間, 它的值好像一直和 ft 相同, ota 或 app 重裝之后, 這里的ft和ut可能會(huì)改變。
  • sharedUserId:應(yīng)用設(shè)置的shanre uid,沒有設(shè)置的情況下,該字段為:userid
  • isOrphaned:Indicates if the package that installed this app has been uninstalled
  • version 是 app 的版本號(hào)信息, 也就是在 AndroidManifest.xml 里配置的 android:versioncode
  • sigs 塊里的 count 表示這個(gè) app 有多少個(gè)簽名信息, 有的 app 可能會(huì)被多個(gè)證書簽名。cert 里的 index 表示這個(gè) app 使用的證書的序號(hào), 當(dāng)系統(tǒng)發(fā)現(xiàn)一個(gè)新的證書, 這個(gè)號(hào)就會(huì)加1, key 是 app 使用的證書內(nèi)容的 ascii 碼值。PKMS 在掃 apk 文件過程中, 如果發(fā)現(xiàn)它和之前掃描到的 apk 使用的是相同的簽名證書, 這里就只會(huì)有個(gè) index 的值, 并沒有 key 的值。擁有相同的 index 的 package, 表明它們使用的是相同的簽名
        <sigs count="1" schemeVersion="3">
            <cert index="0" key="308204a8...90b1b357" />
        </sigs>
  • perms 塊里是這個(gè) app 擁有的權(quán)限, 對(duì)于一般的 app, 這些權(quán)限是在 AndroidManifest.xml 里寫明的; 那些使用了相同 UID 的 app, 這里的權(quán)限就是所有使用相同 UID 的 app 申請(qǐng)的權(quán)限的總和。 granted表示這個(gè)權(quán)限是不是已經(jīng)被允許

  • proper-signing-keyset 里的 identifier 就是packages.xml中的的 keysets 里 identifier 的值。它是用來標(biāo)明這個(gè) app 使用的是哪個(gè)公鑰。

    <keyset-settings version="1">
        <keys>
            <public-key identifier="1" value="MIIBIDANBgk...xUugvhaRXU89fwZBxxe7IJwIBAw==" />
             ...
            <public-key identifier="16" value="MIGfM...QAB" />
            <public-key identifier="17" value="MIGf...AQAB" />
            <public-key identifier="18" value="MIGf...DAQAB" />
            <public-key identifier="19" value="MIG...AQAB" />
            <public-key identifier="20" value="MIGf...AQAB" />
        </keys>
  • installer 表示該應(yīng)用的安裝來源,默認(rèn)集成的應(yīng)用沒有該字段

** Settings.mPackages **

以上信心,解析后,存儲(chǔ)在Settings.PackageSetting對(duì)象中,添加到Settings.mPackages變量中

final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();

            } else if (userId > 0) {
                packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
                        new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
                        secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
                        pkgPrivateFlags, parentPackageName, null /*childPackageNames*/,
                        null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
                if (PackageManagerService.DEBUG_SETTINGS)
                    Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                            + userId + " pkg=" + packageSetting);
                if (packageSetting == null) {
                    PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding uid "
                            + userId + " while parsing settings at "
                            + parser.getPositionDescription());
                } else {
                    packageSetting.setTimeStamp(timeStamp);//ft, 表示apk上次被修改的時(shí)間
                    packageSetting.firstInstallTime = firstInstallTime;//it, 第一次被安裝的時(shí)間
                    packageSetting.lastUpdateTime = lastUpdateTime;//ut, 上一次更新的時(shí)間
                }

** Settings.mPendingPackages **

如果應(yīng)用聲明了android:sharedUserId,則將應(yīng)用添加到mPendingPackages中

    /**
     * Used to track packages that have a shared user ID that hasn't been read
     * in yet.
     * <p>
     * TODO: make this just a local variable that is passed in during package
     * scanning to make it less confusing.
     */
 private final ArrayList<PackageSetting> mPendingPackages = new ArrayList<>();
if (sharedUserId > 0) {
    packageSetting = new PackageSetting(name.intern(), realName, new File(
                            codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
                            primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                            versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
                            null /*childPackageNames*/, sharedUserId,
                            null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/);
    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);
} 

以上即是packages.xml中package配置項(xiàng)的解析過程,和相關(guān)的數(shù)據(jù)結(jié)構(gòu)。字段較短,沒有一一說明,后續(xù)遇到具體場(chǎng)景再具體分析

對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為:

PackageSetting.png
permissions字段解析

** 數(shù)據(jù)格式為:**

    <permissions>
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        <item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
        <item name="android.permission.REMOTE_AUDIO_PLAYBACK" package="android" protection="2" />
        <item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" package="com.android.providers.downloads" />
        ...
        <item name="com.qiku.android.permission.feedback.ACCESS_FEEDBACK" package="com.qiku.android.feedback" protection="2" />
        <item name="org.simalliance.openmobileapi.BIND_TERMINAL" package="com.mediatek" protection="18" />
        <item name="android.permission.SET_SCREEN_COMPATIBILITY" package="android" protection="2" />
        <item name="android.permission.MEDIA_CONTENT_CONTROL" package="android" protection="18" />
        <item name="android.permission.DELETE_PACKAGES" package="android" protection="18" />
        <item name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" package="android" protection="2" />
        <item name="com.android.voicemail.permission.ADD_VOICEMAIL" package="android" protection="1" />
    </permissions>
  • name 表示權(quán)限的名字
  • package 表示聲明權(quán)限的package
  • protection表示權(quán)限的級(jí)別, 如normal, dangerous之類

解析后,存儲(chǔ)在Settings.mPermissions中


final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>();

mPermissions.png

可以通過pm list permissions -f命令獲取權(quán)限信息

full_k61v1_64_bsp:/ # pm  list permissions -f

+ permission:org.simalliance.openmobileapi.BIND_TERMINAL
  package:com.mediatek
  label:Bind OMAPI Terminal
  description:An app with this permission can bind to the System's OMAPI Terminals. Only SmartcardService should use it.
  protectionLevel:signature|privileged
+ permission:android.permission.SET_SCREEN_COMPATIBILITY
  package:android
  label:null
  description:null
  protectionLevel:signature
+ permission:android.permission.MEDIA_CONTENT_CONTROL
  package:android
  label:null
  description:null
  protectionLevel:signature|privileged
+ permission:android.permission.DELETE_PACKAGES
  package:android
  label:null
  description:null
  protectionLevel:signature|privileged
+ permission:android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED
  package:android
  label:null
  description:null
  protectionLevel:signature
shared-user字段解析

數(shù)據(jù)格式:

    <shared-user name="android.media" userId="10010">
        <sigs count="1" schemeVersion="3">
            <cert index="4" />
        </sigs>
        <perms>
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.USE_RESERVED_DISK" granted="true" flags="0" />
            <item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" />
            <item name="android.permission.FOREGROUND_SERVICE" granted="true" flags="0" />
            <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
            <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
            <item name="android.permission.GET_TASKS" granted="true" flags="0" />
            <item name="android.permission.INTERNET" granted="true" flags="0" />
            <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />
            <item name="android.permission.MANAGE_USB" granted="true" flags="0" />
            <item name="android.permission.ACCESS_ALL_DOWNLOADS" granted="true" flags="0" />
            <item name="android.permission.ACCESS_DOWNLOAD_MANAGER" granted="true" flags="0" />
            <item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_MTP" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
            <item name="android.permission.CLEAR_APP_CACHE" granted="true" flags="0" />
            <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" />
            <item name="android.permission.MODIFY_NETWORK_ACCOUNTING" granted="true" flags="0" />
            <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
            <item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" />
        </perms>
    </shared-user>
  • name:應(yīng)用中設(shè)置的android:sharedUserId的名稱,userId為該名稱在系統(tǒng)中對(duì)應(yīng)的uid,定義在Process.java中
    sigs 和 package 塊里的意思相同

  • erms 表示這個(gè)user所具有的權(quán)限。在開機(jī)掃描 apk 文件時(shí), 它會(huì)將所有使用了相同 uid 的 app 的權(quán)限收集到一起, 然后放在這里。并且最后還會(huì)把這些權(quán)限再下發(fā)給那些使用了相同 uid 的 app。最后的結(jié)果就是, 系統(tǒng)中使用相同 uid 的 app, 它們具有一樣的權(quán)限

解析后,存儲(chǔ)在

final ArrayMap<String, SharedUserSetting> mSharedUsers =  new ArrayMap<String, SharedUserSetting>();

對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)關(guān)系為:

shared-user.png

查找關(guān)系為:

通過name="android.media"獲得SharedUserSetting---獲取PermissionsState---獲取對(duì)應(yīng)的所欲權(quán)限mPermissions
----進(jìn)而獲取單個(gè)權(quán)限相關(guān)的數(shù)據(jù)

preferred-activities#####

數(shù)據(jù)格式為:

<preferred-activities>  
    <item name="com.mx.browser/.MxBrowserActivity" match="200000" set="2">  
        <set name="com.android.browser/.BrowserActivity" />  
        <set name="com.mx.browser/.MxBrowserActivity" />  
        <filter>  
            <action name="android.intent.action.VIEW" />  
            <cat name="android.intent.category.BROWSABLE" />  
            <cat name="android.intent.category.DEFAULT" />  
            <scheme name="http" />  
        </filter>  
    </item>  
</preferred-activities>  

用于記錄系統(tǒng)中,默認(rèn)的默認(rèn)應(yīng)用activity信息

如下圖,解析的

  • item.name 對(duì)應(yīng)PreferredComponent.mComponent

  • <set name=""/>的字段,解析后分別將報(bào)名和activity名稱存入mSetPackages : string[]和mSetClasses : string[]

  • <set name=""/>整個(gè)字段對(duì)應(yīng)mSetComponents

  • match:match="200000"

PreferredActivity類圖依賴關(guān)系.png
updated-package字段解析

系統(tǒng)應(yīng)用會(huì)涉及到系統(tǒng)內(nèi)部的服務(wù)和關(guān)鍵功能,所以它們一般情況下是不能被刪除的,但是可以被升級(jí)。升級(jí)系統(tǒng)應(yīng)用的手段就是安裝一個(gè)包名相同、但版本更高的應(yīng)用在/data/app/下。Android系統(tǒng)為了區(qū)分這種升級(jí)關(guān)系,內(nèi)部維護(hù)了一個(gè)package.xml文件,它會(huì)記錄系統(tǒng)每次開機(jī)時(shí),當(dāng)前所有APK的關(guān)鍵信息;在packages.xml下,標(biāo)簽<updated-package>來標(biāo)識(shí)這種被升級(jí)的系統(tǒng)應(yīng)用。下次系統(tǒng)再次啟動(dòng)時(shí),還會(huì)再掃描系統(tǒng)中的APK信息,將當(dāng)前掃描的信息與上一次APK掃描的信息(存放在packages.xml中)進(jìn)行對(duì)比,就能得知APK的變化信息;系統(tǒng)再根據(jù)這些變化信息來做具體的APK的升級(jí)、刪除等處理

數(shù)據(jù)格式為:

    <updated-package name="com.qiku.multiprocess" codePath="/system/app/QK_MultiProcessProfileML" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="1022" nativeLibraryPath="/system/app/QK_MultiProcessProfileML/lib" userId="10087">
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.BIND_DEVICE_ADMIN" granted="true" flags="0" />
            <item name="com.qiku.configcenter.permission.SEND_NOTIFICATION" granted="true" flags="0" />
            <item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.INSTALL_PACKAGES" granted="true" flags="0" />
            <item name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" granted="true" flags="0" />
            <item name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" granted="true" flags="0" />
              .....
        </perms>
    </updated-package>

 <package name="com.qiku.multiprocess" codePath="/data/app/com.qiku.multiprocess-tHL9np5ad3koIxsvEBlHgg==" nativeLibraryPath="/data/app/com.qiku.multiprocess-tHL9np5ad3koIxsvEBlHgg==/lib" publicFlags="944258695" privateFlags="0" ft="16db9783d40" it="11e8dc5d800" ut="16db978414d" version="1022" userId="10087" installer="root" isOrphaned="true">
        <sigs count="1" schemeVersion="1">
            <cert index="2" />
        </sigs>
        <perms>
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.BIND_DEVICE_ADMIN" granted="true" flags="0" />
            <item name="com.qiku.configcenter.permission.SEND_NOTIFICATION" granted="true" flags="0" />
            <item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.INSTALL_PACKAGES" granted="true" flags="0" />
             ....
            <item name="android.permission.DELETE_PACKAGES" granted="true" flags="0" />
        </perms>
        <proper-signing-keyset identifier="3" />
    </package>

updated-package記錄應(yīng)用升級(jí)前的信息,package記錄升級(jí)后的apk信息。實(shí)際運(yùn)行的是package中的apk的信息,updated-package用來記錄被升級(jí)系統(tǒng)應(yīng)用的原始信息

updated-package中的信息被存儲(chǔ)在mDisabledSysPackages數(shù)據(jù)結(jié)構(gòu)中,存儲(chǔ)的信息數(shù)據(jù)結(jié)構(gòu)也是PackageSetting,跟package一樣

    private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
        new ArrayMap<String, PackageSetting>();
keyset-settings

用來存儲(chǔ)系統(tǒng)中所有應(yīng)用使用的公鑰信息

數(shù)據(jù)格式為:

    <keyset-settings version="1">
        <keys>
            <public-key identifier="1" value="MIIBIDANBgk...xUugvhaRXU89fwZBxxe7IJwIBAw==" />
             ...
            <public-key identifier="16" value="MIGfM...QAB" />
            <public-key identifier="17" value="MIGf...AQAB" />
            <public-key identifier="18" value="MIGf...DAQAB" />
            <public-key identifier="19" value="MIG...AQAB" />
            <public-key identifier="20" value="MIGf...AQAB" />
        </keys>

接續(xù)后存儲(chǔ)在一個(gè)LongSparseArray數(shù)據(jù)結(jié)構(gòu)中,讀取packages.xml中package數(shù)據(jù)信息的時(shí)候,會(huì)通過 <proper-signing-keyset identifier="3" />的identifier
查詢KeySetManagerService.mPublicKeys

KeySetManagerService.java

 private final LongSparseArray<PublicKeyHandle> mPublicKeys;
mPendingPackages數(shù)據(jù)的處理

以上字段解析完畢后,會(huì)將Settings.mPendingPackages的數(shù)據(jù)添加到Settings.mPackages中去,然后清除mPendingPackages的數(shù)據(jù)

        final int N = mPendingPackages.size();

        for (int i = 0; i < N; i++) {
            final PackageSetting p = mPendingPackages.get(i);
            final int sharedUserId = p.getSharedUserId();
            final Object idObj = getUserIdLPr(sharedUserId);
            if (idObj instanceof SharedUserSetting) {//將mPendingPackages的SharedUserSetting數(shù)據(jù),添加到mPacakages中去
                final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
                p.sharedUser = sharedUser;
                p.appId = sharedUser.userId;
                addPackageSettingLPw(p, sharedUser);
            } else if (idObj != null) {
                String msg = "Bad package setting: package " + p.name + " has shared uid "
                        + sharedUserId + " that is not a shared uid\n";
                mReadMessages.append(msg);
                PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
            } else {
                String msg = "Bad package setting: package " + p.name + " has shared uid "
                        + sharedUserId + " that is not defined\n";
                mReadMessages.append(msg);
                PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
            }
        }
        mPendingPackages.clear();//清除mPendingPackages的數(shù)據(jù)

writeKernelMappingLPr

    final File kernelDir = new File("/config/sdcardfs");
    mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;

    void writeKernelMappingLPr() {
        if (mKernelMappingFilename == null) return;
        //遍歷/config/sdcardfs目錄,獲取文件名。文件名為各個(gè)應(yīng)用的包名
        final String[] known = mKernelMappingFilename.list();
        final ArraySet<String> knownSet = new ArraySet<>(known.length);
        for (String name : known) {
            knownSet.add(name);
        }

        for (final PackageSetting ps : mPackages.values()) {
            // Package is actively claimed
            knownSet.remove(ps.name);//刪除已安裝的應(yīng)用的數(shù)據(jù),保留未安裝的應(yīng)用數(shù)據(jù),后續(xù)處理
            writeKernelMappingLPr(ps);
        }

        // Remove any unclaimed mappings
        for (int i = 0; i < knownSet.size(); i++) {
            final String name = knownSet.valueAt(i);
            if (DEBUG_KERNEL) Slog.d(TAG, "Dropping mapping " + name);
            //刪除未安裝的應(yīng)用的數(shù)據(jù),和文件。也就是處理臟數(shù)據(jù)
            mKernelMapping.remove(name);
            new File(mKernelMappingFilename, name).delete();
        }
    }
    private static final class KernelPackageState {
        int appId;
        int[] excludedUserIds;
    }
    /** Map from package name to appId and excluded userids */
    private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();

    void writeKernelMappingLPr(PackageSetting ps) {
        if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
        
        //查詢緩存中,是否含有改應(yīng)用
        KernelPackageState cur = mKernelMapping.get(ps.name);
        final boolean firstTime = cur == null;//如果,緩存中沒有數(shù)據(jù),則表明是第一次
        int[] excludedUserIds = ps.getNotInstalledUserIds();//得到應(yīng)用禁止在其他多用戶空間安裝的用戶空間userid
        final boolean userIdsChanged = firstTime
                || !Arrays.equals(excludedUserIds, cur.excludedUserIds);//判斷應(yīng)用的uid是否發(fā)生變化

        // Package directory
        final File dir = new File(mKernelMappingFilename, ps.name);

        if (firstTime) {//如果,應(yīng)用是第一次執(zhí)行該操作,則創(chuàng)建應(yīng)用對(duì)應(yīng)目錄,并保存對(duì)應(yīng)的KernelPackageState對(duì)象在內(nèi)存中
            dir.mkdir();
            // Create a new mapping state
            cur = new KernelPackageState();
            mKernelMapping.put(ps.name, cur);
        }

        // If mapping is incorrect or non-existent, write the appid file
        if (cur.appId != ps.appId) {//更新應(yīng)用的appid,數(shù)據(jù)存儲(chǔ)在/config/sdcardfs/pkgname/appid 文件中
            final File appIdFile = new File(dir, "appid");
            writeIntToFile(appIdFile, ps.appId);
            if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
        }

        if (userIdsChanged) {//如果firstTime,或者excludedUserIds發(fā)生變化
            // Build the exclusion list -- the ids to add to the exclusion list
            //excludeUserIds發(fā)生變化,更新/config/sdcardfs/pkgname/excluded_userids文件中的數(shù)據(jù)
            for (int i = 0; i < excludedUserIds.length; i++) {
                if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds,
                        excludedUserIds[i])) {
                    writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
                    if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
                            + ps.name + "/excluded_userids");
                }
            }
            // Build the inclusion list -- the ids to remove from the exclusion list
            //excludedUserIds表示當(dāng)前應(yīng)用當(dāng)前的id數(shù)據(jù)
            //cur.excludedUserIds[i] 表示上一次緩存的數(shù)據(jù)
            //相比上次去除的userid,存儲(chǔ)在/config/sdcardfs/pkgname/clear_userid文件中
            if (cur.excludedUserIds != null) {
                for (int i = 0; i < cur.excludedUserIds.length; i++) {
                    if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) {
                        writeIntToFile(new File(dir, "clear_userid"),
                                cur.excludedUserIds[i]);
                        if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
                                + ps.name + "/clear_userid");

                    }
                }
            }
            cur.excludedUserIds = excludedUserIds;
        }
    }

以搜狐輸入法為例,看下對(duì)應(yīng)目錄下文件存儲(chǔ)的數(shù)據(jù)

130|full_k61v1_64_bsp:/config/sdcardfs # cd com.sohu.inputmethod.sogou

full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # ls
appid clear_userid excluded_userids

//當(dāng)前搜狗輸入法的appid
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat appid
10075

//表示搜狗輸入法,禁止在那些多用戶空間安裝
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat excluded_userids                                                           <
1003 1002 1001 1000 999 998 997

//文件內(nèi)容無權(quán)限查看,內(nèi)容是當(dāng)前excluded_userids 文件內(nèi)容與上次文件內(nèi)容的差集
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat clear_userid                                                              
cat: clear_userid: Permission denied

驗(yàn)證系統(tǒng)指紋

           final VersionInfo ver = mSettings.getInternalVersion();
            //系統(tǒng)通過判斷當(dāng)前手機(jī)版本的指紋和上一個(gè)版本的指紋信息是否相同,來判斷是否屬于系統(tǒng)升級(jí)
            mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);

            if (mIsUpgrade) {
                logCriticalInfo(Log.INFO,
                        "Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
            }

            // when upgrading from pre-M, promote system app permissions from install to runtime
           //如果,系統(tǒng)從M版本升級(jí)上來,則將系統(tǒng)app的權(quán)限從安裝權(quán)限提升到運(yùn)行時(shí)權(quán)限
            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

            // When upgrading from pre-N, we need to handle package extraction like first boot,
            // as there is no profiling data available.
            //用于處理從N版本升級(jí)上來的情況
            mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;

            mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;

            // save off the names of pre-existing system packages prior to scanning; we don't
            // want to automatically grant runtime permissions for new system apps
            if (mPromoteSystemApps) {//處理從M版本升級(jí)上來時(shí),系統(tǒng)app的權(quán)限問題
                Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
                while (pkgSettingIter.hasNext()) {
                    PackageSetting ps = pkgSettingIter.next();
                    if (isSystemApp(ps)) {
                        mExistingSystemPackages.add(ps.name);
                    }
                }
            }

            mCacheDir = preparePackageParserCache(mIsUpgrade);

如果是系統(tǒng)判斷是升級(jí)了,則會(huì)刪除/data/system/package_cache目錄的應(yīng)用緩存數(shù)據(jù)

    private static File preparePackageParserCache(boolean isUpgrade) {
      ....

        // If this is a system upgrade scenario, delete the contents of the package cache dir.
        // This also serves to "GC" unused entries when the package cache version changes (which
        // can only happen during upgrades).
        if (isUpgrade) {
            FileUtils.deleteContents(cacheBaseDir);
        }

關(guān)于系統(tǒng)版本指紋驗(yàn)證,在實(shí)際項(xiàng)目中遇到一個(gè)問題:
很多手機(jī)廠商,因?yàn)楦鞣N原因,在OTA升級(jí)時(shí),會(huì)固定系統(tǒng)指紋信息; 也即,在升級(jí)前和升級(jí)后,將系統(tǒng)指紋信息保持不變

這就導(dǎo)致了一個(gè)問題:

Android8.0開始增加了一個(gè)應(yīng)用cache路徑/data/system/package_cache,如果是系統(tǒng)升級(jí)了,則會(huì)清除這個(gè)cache下的緩存信息
如果fingerprint未變,isUpgrade始終為false,pms就會(huì)繼續(xù)讀取這個(gè)緩存信息

如果在升級(jí)過程中,某個(gè)應(yīng)用更新了組件相關(guān)信息,因?yàn)樽x取的還是緩存的應(yīng)用組件信息,則apk就會(huì)報(bào)錯(cuò)找不到相關(guān)組件

另外OTA之后進(jìn)行一次恢復(fù)出廠設(shè)置之后應(yīng)用也能恢復(fù)更新后的基線,因?yàn)榛謴?fù)出廠設(shè)置之后data下面的數(shù)據(jù)會(huì)被清除

總結(jié)

上述流程完成的主要工作如下:

  1. 簡(jiǎn)單初始化PMS依賴的相關(guān)變量
  2. addSharedUserLPw創(chuàng)建共享uid相關(guān)的數(shù)據(jù)結(jié)構(gòu)
  3. 初始化dex優(yōu)化服務(wù)類,解析SystemConfig文件
  4. SystemConfig讀取配置feature配置,系統(tǒng)共享庫,uid權(quán)限對(duì)應(yīng)關(guān)系等數(shù)據(jù)
  5. 創(chuàng)建PMS的HandlerThread和系統(tǒng)共享庫處理
  6. readLPw解析packages.xml文件
  7. 驗(yàn)證系統(tǒng)指紋

本章節(jié),主要解析PMS初始化過程中,解析packages.xml的文件內(nèi)容,下一章節(jié),我們重點(diǎn)解析PMS掃描apk的過程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容