PackageManagerService之app数据类(Settings)分析

PMS系列:
1、PackageManagerService服务框架详解
2、PackageManagerService启动分析
3、本文PackageManagerService之app数据类(Settings)分析
4、PackageManagerService扫描安装apk详解
5、PackageManagerService根据权限等级管理权限(默认赋予apk权限)

从PMS的初始化分析,我们知道了PMS中的这个Settings类是用来保存apk的信息
本文主要分析这个Settings类,(frameworks\base\services\core\java\com\android\server\pm\Settings.java)
本文主线是PackageManagerService.java构造函数中,按PMS的调用mSettings的顺序来分析Settings类,以了解Settings主要存了什么

一、new Settings

在PackageManagerService.java构造函数中开始定义Settings

final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<String, PackageParser.Package>();
mSettings = new Settings(mPackages);

创建

Settings(Object lock) {
        this(Environment.getDataDirectory(), lock); //Environment.getDataDirectory() "/data"
    }

    Settings(File dataDir, Object lock) {
        mLock = lock;
    //给data目录权限
        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
    //创建data/system目录
        mSystemDir = new File(dataDir, "system");
        mSystemDir.mkdirs();
    //给mSystemDir设置权限
        FileUtils.setPermissions(mSystemDir.toString(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
    //packages-backup.xml是packages.xml的备份,当apk信息写入backup完成后,再创建packages.xml以免数据丢失
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
    //packages.list保存系统中存在的所有非系统自带的APK信息
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);

    //sdcardfs相关
        final File kernelDir = new File("/config/sdcardfs");
        mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
        
    //packages-stopped.xml用于描述系统中强行停止运行的package信息,backup也是备份文件
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
    }

二、addSharedUserLPw

继续看PackageManagerService

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

我们来看Settings类的添加uid的方法

//name和uid对应 "android.uid.system", Process.SYSTEM_UID(1000)
//pkgFlags均为:ApplicationInfo.FLAG_SYSTEM表示系统包内应用
//pkgPrivateFlags均为ApplicationInfo.PRIVATE_FLAG_PRIVILEGED,可以拥有特殊权限
//保存到mSharedUsers
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;
        }
    //根据参数构建SharedUserSetting
        s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
        s.userId = uid;
        if (addUserIdLPw(uid, s, name)) {
        //按name和s,存入map中
            mSharedUsers.put(name, s);
            return s;
        }
        return null;
    }

//最后被保存到了mUserIds和mOtherUserIds中
//addUserIdLPw在getPackageLPw也有调用
private boolean addUserIdLPw(int uid, Object obj, Object name) {
        if (uid > Process.LAST_APPLICATION_UID) {//最大19999
            return false;
        }
    //FIRST_APPLICATION_UID = 10000普通的id,即应用程序从10000开始,10000以下是系统进程id
        if (uid >= Process.FIRST_APPLICATION_UID) {
            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 {
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR,
                        "Adding duplicate shared id: " + uid
                                + " name=" + name);
                return false;
            }
        //保存
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }

mSharedUsers(map)以name为索引管理SharedUserSetting
mUserIds(ArrayList)以uid为索引管理SharedUserSettings,mUserIds保存了系统中已经分配的uid对应的SharedUserSetting结构。每次分配时总是从第一个开始轮询,找到第一个空闲的位置i,然后加上FIRST_APPLICATION_UID即可
mOtherUserIds(SparseArray)以uid为索引管理SharedUserSettings
接下来看看SharedUserSettings

final class SharedUserSetting extends SettingBase {
    final String name;
    int userId;
    int uidFlags;
    int uidPrivateFlags;

    final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();

    final PackageSignatures signatures = new PackageSignatures();

    SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
        super(_pkgFlags, _pkgPrivateFlags);
        uidFlags =  _pkgFlags;
        uidPrivateFlags = _pkgPrivateFlags;
        name = _name;
    }

    void removePackage(PackageSetting packageSetting) {
        if (packages.remove(packageSetting)) {
           ...
        }
    }

    void addPackage(PackageSetting packageSetting) {
        if (packages.add(packageSetting)) {
            ...
        }
    }
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    package="com.android.settings"
    coreApp="true"
    //指定sharedUserId,PKMS会为其创建了SharedUserSettings
    android:sharedUserId="android.uid.system"/>

1.如果在 AndroidManifest.xml 中指定了sharedUserId,那么先查看全局list中(mSharedUsers)是否该uid对应的SharedUserSetting数据结构,若没有则新分配一个uid,创建 SharedUserSetting 并保存到全局全局 list(mSharedUsers)中
2.SharedUserSetting继承自SettingBase,SettingBase持有PermissionsState对象,用于表示可用的权限
3.PackageSettings中持有的是单个Package独有的权限
4.SharedUserSettings中持有的是一组Package共有的权限
5.SharedUserSetting将PackageSettings添加到packages(ArraySet<PackageSetting>)中,表示PackageSetting为其中的共享者。所以相同的sharedUserId运行到同一uid并可以数据共享

三、mSettings.mPermissions

SystemConfig systemConfig = SystemConfig.getInstance();
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                    = systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
    SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
    BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
    mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
    bp.setGids(perm.gids, perm.perUser);
}
}

1、先看systemConfig是什么

public static SystemConfig getInstance() {
    ...
    sInstance = new SystemConfig();
    ....
}
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);
        // Allow ODM to customize system configs around libs, features and apps
        int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
        // Only allow OEM to customize features
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);

        /// M: Only allow libraries
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "sysconfig"), ALLOW_LIBS);
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), ALLOW_LIBS);
    }

读取/etc目录下sysconfig、permissions...等的xml权限文件,最后保存到mPermissions
2.for (int i=0; i<permConfig.size(); i++)
遍历,将android权限与gid关联

四、readLPw()

readLPw:解析packages.xml、packages-stopped.xml

mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false));
 boolean readLPw(@NonNull List<UserInfo> users) {
        ...
        //解析xml,其中很多read方法,都是解析对应tag的
    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")) {
        readPermissionsLPw(mPermissions, parser);
    } else if (tagName.equals("permission-trees")) {
        readPermissionsLPw(mPermissionTrees, parser);
    } else if (tagName.equals("shared-user")) {
        readSharedUserLPw(parser);
    } else if (tagName.equals("preferred-packages")) {
        // no longer used.
    } 
    ...

五、mSettings.writeLPr();

将安装信息写回pakcage.xml

void writeLPr() {
    ...
        //都是一些xml操作
        try {
            FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
            BufferedOutputStream str = new BufferedOutputStream(fstr);

            //XmlSerializer serializer = XmlUtils.serializerInstance();
            XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(str, StandardCharsets.UTF_8.name());
            serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
        //开始的tag
            serializer.startTag(null, "packages");

            for (int i = 0; i < mVersion.size(); i++) {
                final String volumeUuid = mVersion.keyAt(i);
                final VersionInfo ver = mVersion.valueAt(i);
        //TAG:version
                serializer.startTag(null, TAG_VERSION);
                XmlUtils.writeStringAttribute(serializer, ATTR_VOLUME_UUID, volumeUuid);
                XmlUtils.writeIntAttribute(serializer, ATTR_SDK_VERSION, ver.sdkVersion);
                XmlUtils.writeIntAttribute(serializer, ATTR_DATABASE_VERSION, ver.databaseVersion);
                XmlUtils.writeStringAttribute(serializer, ATTR_FINGERPRINT, ver.fingerprint);
                serializer.endTag(null, TAG_VERSION);
            }
        //TAG:verifier
            if (mVerifierDeviceIdentity != null) {
                serializer.startTag(null, "verifier");
                serializer.attribute(null, "device", mVerifierDeviceIdentity.toString());
                serializer.endTag(null, "verifier");
            }
        //等等各种数据
            ...

            //结TAG
            serializer.endTag(null, "packages");

            serializer.endDocument();

            str.flush();
            FileUtils.sync(fstr);
            str.close();

            //重新设置权限
            mBackupSettingsFilename.delete();
            FileUtils.setPermissions(mSettingsFilename.toString(),
                    FileUtils.S_IRUSR|FileUtils.S_IWUSR
                    |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
                    -1, -1);
        //刷新packagelist等数据
            writeKernelMappingLPr();
            writePackageListLPr();
            writeAllUsersPackageRestrictionsLPr();
            writeAllRuntimePermissionsLPr();
            return;

        ...
    }

小结

本文根据PMS初始化时候调用的Settings相关方法对,Settings类初步分析:
1、Settings:创建package.xml、packages-stopped.xml等
2、addSharedUserLPw:添加SharedUserSettings数据结构
3、mSettings.mPermissions:权限数据读取
4、readLPw():读取packag.xml
5、writeLPr():写入已安装apk信息到packag.xml

Read the fucking sources code!

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

推荐阅读更多精彩内容