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

代码基于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;
    }
  • factoryTest:表示是否运行在工厂模式

  • onlyCore:表示是否运行在只解析core应用的模式下,也就是加密模式下

  • 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对象,并添加到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);
        }
        }
  • 主要完成变量的赋值;初始化UserManagerService的,UserManagerService用于管理android的多用户;初始化PermissionManagerService,用于管理应用的权限

  • 初始化mDefaultPermissionPolicy,用于管理平台组件默认获取运行时权限。例如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主要用于初始化一个SharedUserSetting数据结构,用于保存声明了同一个android:sharedUserId="android.uid.system" 的应用信息

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

上图中的userId,指的是uid

一个应用声明了与其他应用共享uid,主要有以下几个作用:

  • 两个或多个声明了同一种sharedUserIds的APK可共享彼此的数据,并且可运行在同一进程中

  • 更重要的是,通过声明特定的sharedUserId,该APK所在进程将被赋予指定的UID; SystemUI声明了system的uid,运行SystemUI的进程就可享有system用户所对应的权限(实际上就是将该进程的uid设置为system的uid)了

  • AndroidManifest.xml中声明sharedUserId外,APK在编译时还必须使用对应的证书进行签名。例如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) {//普通应用,其值大于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.xml,packages_backup.xml: PKMS扫描完目标文件夹后会创建该文件。
    当系统进行程序安装、卸载和更新等操作时,均会更新该文件。该文件保存了系统中与package相关的一些信息。
    backup是临时文件。PKMS先把数据写到backup中,信息都写成功后再改名成非backup的文件。
    其目的是防止在写文件过程中出错,导致信息丢失;如果存在backup文件,则表示写入过程中,被异常中指。

  • packages.list:描述系统中存在的所有非系统自带的APK的信息。当这些程序有变动时,PKMS就会更新该文件。

  • packages-stopped.xml:从系统自带的设置程序中进入应用程序页面,然后在选择强制停止(ForceStop)某个应用时,系统会将该应用的相关信息记录到此文件中。也就是该文件保存系统中被用户强制停止的Package的信息。

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>

** 各个字段意义: **

  • name:应用包名
  • codePath:应用安装位置,可以安装在:/system/priv-app/,/system/app/,/vendor/overlay,/system/presetapp,/data/app目录
  • nativeLibraryPath:表示 app 使用的 xxx.so 库存放的位置
  • primaryCpuAbi: app 以哪种 abi 架构运行
  • ft 表示 apk 文件上次被更改的时间, it 表示 app 第一次安装的时间, ut 表示 app 上次被更新时间, 它的值好像一直和 ft 相同, ota 或 app 重装之后, 这里的ft和ut可能会改变。
  • sharedUserId:应用设置的shanre uid,没有设置的情况下,该字段为:userid
  • isOrphaned:Indicates if the package that installed this app has been uninstalled
  • version 是 app 的版本号信息, 也就是在 AndroidManifest.xml 里配置的 android:versioncode
  • sigs 块里的 count 表示这个 app 有多少个签名信息, 有的 app 可能会被多个证书签名。cert 里的 index 表示这个 app 使用的证书的序号, 当系统发现一个新的证书, 这个号就会加1, key 是 app 使用的证书内容的 ascii 码值。PKMS 在扫 apk 文件过程中, 如果发现它和之前扫描到的 apk 使用的是相同的签名证书, 这里就只会有个 index 的值, 并没有 key 的值。拥有相同的 index 的 package, 表明它们使用的是相同的签名
        <sigs count="1" schemeVersion="3">
            <cert index="0" key="308204a8...90b1b357" />
        </sigs>
  • perms 块里是这个 app 拥有的权限, 对于一般的 app, 这些权限是在 AndroidManifest.xml 里写明的; 那些使用了相同 UID 的 app, 这里的权限就是所有使用相同 UID 的 app 申请的权限的总和。 granted表示这个权限是不是已经被允许

  • proper-signing-keyset 里的 identifier 就是packages.xml中的的 keysets 里 identifier 的值。它是用来标明这个 app 使用的是哪个公钥。

    <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 表示该应用的安装来源,默认集成的应用没有该字段

** 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>
  • name 表示权限的名字
  • package 表示声明权限的package
  • protection表示权限的级别, 如normal, dangerous之类

解析后,存储在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>
  • name:应用中设置的android:sharedUserId的名称,userId为该名称在系统中对应的uid,定义在Process.java中
    sigs 和 package 块里的意思相同

  • erms 表示这个user所具有的权限。在开机扫描 apk 文件时, 它会将所有使用了相同 uid 的 app 的权限收集到一起, 然后放在这里。并且最后还会把这些权限再下发给那些使用了相同 uid 的 app。最后的结果就是, 系统中使用相同 uid 的 app, 它们具有一样的权限

解析后,存储在

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

如下图,解析的

  • item.name 对应PreferredComponent.mComponent

  • <set name=""/>的字段,解析后分别将报名和activity名称存入mSetPackages : string[]和mSetClasses : string[]

  • <set name=""/>整个字段对应mSetComponents

  • match:match="200000"

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的过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350