AndroidAndroid OS

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

2019-10-12  本文已影响0人  古风子

代码基于MTK Android 9.x

目录结构

  1 以上过程的主要调用序列图如下:
   2 mOnlyCore
   3 SystemServer.startBootstrapServices
   4 PackageManagerService.main
   5 new PackageManagerService
       5.1 简单初始化工作
       5.2 addSharedUserLPw
           5.2.1 uid和权限的关系
       5.3 初始化dex优化服务类,解析SystemConfig文件
       5.4 SystemConfig读取配置文件
           5.4.1 feature配置文件
           5.4.2 系统库文件
           5.4.3 权限和gid映射文件
           5.4.4 assign-permission
           5.4.5 allow-in-power-save
       5.5 创建PMS的HandlerThread和系统共享库处理
       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数据的处理
           5.6.3 writeKernelMappingLPr
       5.7 验证系统指纹
   6 总结

以上过程的主要调用序列图如下:

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;
        }

只有当vold.decrypt属性的值为"trigger_restart_min_framework"和“1”时,mOnlyCore才为true

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

mOnlyCore为true表示,PMS只会对声明了 coreApp="true"的应用进行解析

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;
    }
    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对象,并添加到ServiceManager中去

new PackageManagerService

简单初始化工作

        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);
        }
        }

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主要用于初始化一个SharedUserSetting数据结构,用于保存声明了同一个android:sharedUserId="android.uid.system" 的应用信息

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

上图中的userId,指的是uid

一个应用声明了与其他应用共享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) {//普通应用,其值大于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 {//系统应用,其值小于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;
    }

除了创建一个map外,settings中还会创建另外两个数据结构,mUserIds和mOtherUserIds,这两个数据结构,通过数组下表映射进行查询,速度比mSharedUsers更快
mUserIds是一个ArrayList,存储普通应用,以(uid-Process.FIRST_APPLICATION_UID)为index,SharedUserSetting为value
mOtherUserIds是一个,SparseArray,存储系统应用,以uid为index,SharedUserSetting为value

uid和权限的关系

之所以不同的应用,通过指定相同的uid,可以共享权限,是因为,系统中是通过uid为映射,存储应用的权限数据的。不同的应用,具有同一个uid,查到的权限数据是相同的

初始化dex优化服务类,解析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优化相关类

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,解析各个目录下/etc/permissions和/etc/sysconfig下的各个配置文件

    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);
      ....
    }

读取的文件目录的顺序为/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..文件中中声明了手机能够支持的的硬件特性,例如支持camera、支持蓝牙等

数据格式为:

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

解析后的数据存储在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查看手机支持的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

系统库文件

library声明的是系统提供的Java库,应用程序运行时候必须要链接这些库,该工作由系统自动完成

数据格式为:

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

数据解析后,存储在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命令,查看系统中支持的系统库

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

权限和gid映射文件

permission和group用于建立Linux层gid和Android层pemission之间的映射关系

具有android.permission.BLUETOOTH_ADMIN权限的应用,将获得net_bt_admin组权限

数据格式为:

    <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的名称和数字的对应是在system\core\include\private\android_filesystem_config.h中定义的

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

解析后,数据存储在mPermissions中,PermissionEntry存储了改权限关联的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

赋予对应uid相应的权限。如果下面一行表示uid为media,那么就赋予它ACCESS_SURFACE_FLINGER的权限,其实就是把它加到对应的用户组中

数据格式为:


<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数据结构中

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

允许应用在省电模式下,访问网络

数据格式为:

 <!-- 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" />

解析后,存储在

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

创建PMS的HandlerThread和系统共享库处理

            //创建PMS的HanderThread,进行异步任务的处理
            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);
           
            //获取上文所说的系统共享库
            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中各个参数的意义:

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) {//如果是静态类型的系统共享包,各个类型的意义见下面代码解释
                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;

上述代码逻辑对应的数据结构为:

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

即可以通过name-->得到该系统共享包对应的各个版本的SharedLibraryEntry信息

readLPw

LPw, local printwrite

改方法主要对三个文件进行读取

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

在该方法开始的时候,会判断packages.xml,packages_backup.xml是否存在,如果都不能存在的话,会直接返回false

判断文件是否存在

        Log.d(TAG, Log.getStackTraceString(new Throwable()));
        FileInputStream str = null;
        if (mBackupSettingsFilename.exists()) {
            try {
                //如果packages_backup.xml文件存在,则设置成,读取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两个文件都存在,则删除packages.xml文件,因为,正常的文件写入流程可能已经被中断
                    // 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.
            }
        }
        //清楚本都数据信息,每个属性代表的字段,后面讲解
        mPendingPackages.clear();
        mPastSignatures.clear();
        mKeySetRefs.clear();
        mInstallerPackages.clear();

        try {
            if (str == null) {
                //如果packages_backup.xml和packages.xml两个文件都不存在,则读取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
                   //获取系统指纹,此时packages.xml文件不存在,从系统属性中获取系统指纹
                    findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
                    findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
                    return false;
                }
                str = new FileInputStream(mSettingsFilename);
            }

获取系统指纹的方法为:
先判断ro.build.fingerprint属性的值,不为空的情况下,通过多个属性值进行拼接
处于各种目的,有的项目会将ro.build.fingerprint设置成固定的值

    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文件,如下所示,我们一个一个的分析,packages.xml文件的各个字段的解析过程

 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字段解析

数据格式为:

    <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>

** 各个字段意义: **

        <sigs count="1" schemeVersion="3">
            <cert index="0" key="308204a8...90b1b357" />
        </sigs>
    <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>

** Settings.mPackages **

以上信心,解析后,存储在Settings.PackageSetting对象中,添加到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上次被修改的时间
                    packageSetting.firstInstallTime = firstInstallTime;//it, 第一次被安装的时间
                    packageSetting.lastUpdateTime = lastUpdateTime;//ut, 上一次更新的时间
                }

** Settings.mPendingPackages **

如果应用声明了android:sharedUserId,则将应用添加到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配置项的解析过程,和相关的数据结构。字段较短,没有一一说明,后续遇到具体场景再具体分析

对应的数据结构为:

PackageSetting.png
permissions字段解析

** 数据格式为:**

    <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>

解析后,存储在Settings.mPermissions中


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

mPermissions.png

可以通过pm list permissions -f命令获取权限信息

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字段解析

数据格式:

    <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>

解析后,存储在

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

对应的数据结构关系为:

shared-user.png

查找关系为:

通过name="android.media"获得SharedUserSetting---获取PermissionsState---获取对应的所欲权限mPermissions
----进而获取单个权限相关的数据

preferred-activities#####

数据格式为:

<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>  

用于记录系统中,默认的默认应用activity信息

如下图,解析的

PreferredActivity类图依赖关系.png
updated-package字段解析

系统应用会涉及到系统内部的服务和关键功能,所以它们一般情况下是不能被删除的,但是可以被升级。升级系统应用的手段就是安装一个包名相同、但版本更高的应用在/data/app/下。Android系统为了区分这种升级关系,内部维护了一个package.xml文件,它会记录系统每次开机时,当前所有APK的关键信息;在packages.xml下,标签<updated-package>来标识这种被升级的系统应用。下次系统再次启动时,还会再扫描系统中的APK信息,将当前扫描的信息与上一次APK扫描的信息(存放在packages.xml中)进行对比,就能得知APK的变化信息;系统再根据这些变化信息来做具体的APK的升级、删除等处理

数据格式为:

    <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记录应用升级前的信息,package记录升级后的apk信息。实际运行的是package中的apk的信息,updated-package用来记录被升级系统应用的原始信息

updated-package中的信息被存储在mDisabledSysPackages数据结构中,存储的信息数据结构也是PackageSetting,跟package一样

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

用来存储系统中所有应用使用的公钥信息

数据格式为:

    <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>

接续后存储在一个LongSparseArray数据结构中,读取packages.xml中package数据信息的时候,会通过 <proper-signing-keyset identifier="3" />的identifier
查询KeySetManagerService.mPublicKeys

KeySetManagerService.java

 private final LongSparseArray<PublicKeyHandle> mPublicKeys;
mPendingPackages数据的处理

以上字段解析完毕后,会将Settings.mPendingPackages的数据添加到Settings.mPackages中去,然后清除mPendingPackages的数据

        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数据,添加到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的数据

writeKernelMappingLPr

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

    void writeKernelMappingLPr() {
        if (mKernelMappingFilename == null) return;
        //遍历/config/sdcardfs目录,获取文件名。文件名为各个应用的包名
        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);//删除已安装的应用的数据,保留未安装的应用数据,后续处理
            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);
            //删除未安装的应用的数据,和文件。也就是处理脏数据
            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;
        
        //查询缓存中,是否含有改应用
        KernelPackageState cur = mKernelMapping.get(ps.name);
        final boolean firstTime = cur == null;//如果,缓存中没有数据,则表明是第一次
        int[] excludedUserIds = ps.getNotInstalledUserIds();//得到应用禁止在其他多用户空间安装的用户空间userid
        final boolean userIdsChanged = firstTime
                || !Arrays.equals(excludedUserIds, cur.excludedUserIds);//判断应用的uid是否发生变化

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

        if (firstTime) {//如果,应用是第一次执行该操作,则创建应用对应目录,并保存对应的KernelPackageState对象在内存中
            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) {//更新应用的appid,数据存储在/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发生变化
            // Build the exclusion list -- the ids to add to the exclusion list
            //excludeUserIds发生变化,更新/config/sdcardfs/pkgname/excluded_userids文件中的数据
            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表示当前应用当前的id数据
            //cur.excludedUserIds[i] 表示上一次缓存的数据
            //相比上次去除的userid,存储在/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;
        }
    }

以搜狐输入法为例,看下对应目录下文件存储的数据

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

//当前搜狗输入法的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

//文件内容无权限查看,内容是当前excluded_userids 文件内容与上次文件内容的差集
full_k61v1_64_bsp:/config/sdcardfs/com.sohu.inputmethod.sogou # cat clear_userid                                                              
cat: clear_userid: Permission denied

验证系统指纹

           final VersionInfo ver = mSettings.getInternalVersion();
            //系统通过判断当前手机版本的指纹和上一个版本的指纹信息是否相同,来判断是否属于系统升级
            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
           //如果,系统从M版本升级上来,则将系统app的权限从安装权限提升到运行时权限
            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版本升级上来的情况
            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版本升级上来时,系统app的权限问题
                Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
                while (pkgSettingIter.hasNext()) {
                    PackageSetting ps = pkgSettingIter.next();
                    if (isSystemApp(ps)) {
                        mExistingSystemPackages.add(ps.name);
                    }
                }
            }

            mCacheDir = preparePackageParserCache(mIsUpgrade);

如果是系统判断是升级了,则会删除/data/system/package_cache目录的应用缓存数据

    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);
        }

关于系统版本指纹验证,在实际项目中遇到一个问题:
很多手机厂商,因为各种原因,在OTA升级时,会固定系统指纹信息; 也即,在升级前和升级后,将系统指纹信息保持不变

这就导致了一个问题:

Android8.0开始增加了一个应用cache路径/data/system/package_cache,如果是系统升级了,则会清除这个cache下的缓存信息
如果fingerprint未变,isUpgrade始终为false,pms就会继续读取这个缓存信息

如果在升级过程中,某个应用更新了组件相关信息,因为读取的还是缓存的应用组件信息,则apk就会报错找不到相关组件

另外OTA之后进行一次恢复出厂设置之后应用也能恢复更新后的基线,因为恢复出厂设置之后data下面的数据会被清除

总结

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

  1. 简单初始化PMS依赖的相关变量
  2. addSharedUserLPw创建共享uid相关的数据结构
  3. 初始化dex优化服务类,解析SystemConfig文件
  4. SystemConfig读取配置feature配置,系统共享库,uid权限对应关系等数据
  5. 创建PMS的HandlerThread和系统共享库处理
  6. readLPw解析packages.xml文件
  7. 验证系统指纹

本章节,主要解析PMS初始化过程中,解析packages.xml的文件内容,下一章节,我们重点解析PMS扫描apk的过程

上一篇下一篇

猜你喜欢

热点阅读