PackageManagerService之app数据类(Set

作者: Yink_Liu | 来源:发表于2017-09-21 20:07 被阅读0次

    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!

    相关文章

      网友评论

        本文标题:PackageManagerService之app数据类(Set

        本文链接:https://www.haomeiwen.com/subject/ekmtextx.html