Android 11—-SystemConfig.java analysis

SystemConfig.java is a very important class in PKMS, which is used to parse some system configuration information, and then assign the parsed results to each data structure in SystemConfig for us to query. First look at its construction method (in the construction method of PKMS, the configuration file will be parsed by calling the construction method of SystemConfig):

frameworks/base/core/java/com/android/server/SystemConfig.java
    SystemConfig() {
        TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
        log.traceBegin("readAllPermissions");
        try {
            readAllPermissions();
        } finally {
            log.traceEnd();
        }
    }

Call the readAllPermissions() method to read the system configuration.

 private void readAllPermissions() {
        // Read configuration from system
        readPermissions(Environment. buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);//Read system/etc/sysconfig directory

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

        // Vendors are only allowed to customize these
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
                | ALLOW_ASSOCIATIONS;
        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
            // For backward compatibility
            vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
        }
        readPermissions(Environment. buildPath(
                Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);//Read vendor/etc/sysconfig directory
        readPermissions(Environment. buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);//Read vendor/etc/permissions directory

        String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, "");//vendor/etc/vintf/manifest_SKU.xml If SKU is defined, then SKU is the value of attribute ro.boot.product.vendor.sku, according to this value to read the corresponding directory
        if (!vendorSkuProperty. isEmpty()) {
            String vendorSkuDir = "sku_" + vendorSkuProperty;
            readPermissions(Environment. buildPath(
                    Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir),
                    vendorPermissionFlag);
            readPermissions(Environment. buildPath(
                    Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir),
                    vendorPermissionFlag);
        }

        // Allow ODM to customize system configs as much as Vendor, because /odm is another
        // vendor partition other than /vendor.
        int odmPermissionFlag = vendorPermissionFlag;
        readPermissions(Environment. buildPath(
                Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);//Read odm/etc/sysconfig directory
        readPermissions(Environment. buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);//Read odm/etc/permissions directory

        String skuProperty = SystemProperties.get(SKU_PROPERTY, "");//odm/etc/vintf/manifest_SKU.xml If SKU is defined, then SKU is the value of attribute ro.boot.product.hardware.sku, according to this value to read the corresponding directory
        if (!skuProperty. isEmpty()) {
            String skuDir = "sku_" + skuProperty;

            readPermissions(Environment. buildPath(
                    Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);
            readPermissions(Environment. buildPath(
                    Environment.getOdmDirectory(), "etc", "permissions", skuDir),
                    odmPermissionFlag);
        }

        // Allow OEM to customize these
        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
        readPermissions(Environment. buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);//Read oem/etc/sysconfig directory
        readPermissions(Environment. buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);//Read oem/etc/permissions directory

        // Allow Product to customize all system configs
        readPermissions(Environment. buildPath(
                Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL);//Read product/etc/sysconfig directory
        readPermissions(Environment. buildPath(
                Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);//Read product/etc/permissions directory

        // Allow /system_ext to customize all system configs
        readPermissions(Environment. buildPath(
                Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL);//Read system_ext/etc/sysconfig directory
        readPermissions(Environment. buildPath(
                Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);//Read system_ext/etc/permissions directory

        // Skip loading configuration from apex if it is not a system process.
        if (!isSystemProcess()) {//Must be in the system process
            return;
        }
        // Read configuration of libs from apex module.
        // TODO: Use a solid way to filter apex module folders?
        for (File f: FileUtils. listFilesOrEmpty(Environment. getApexDirectory())) {
            if (f.isFile() || f.getPath().contains("@")) {//filter files and path files containing @
                continue;
            }
            readPermissions(Environment.buildPath(f, "etc", "permissions"), ALLOW_LIBS);//Read the apex/etc/permissions directory
        }
    }

It can be seen that the readAllPermissions() method mainly reads the files in the etc/permissions and etc/sysconfig directories in each directory, and the permissionFlag parameter indicates the tag type that is allowed to be parsed in the xml file.

 public void readPermissions(File libraryDir, int permissionFlag) {
        // Read permissions from given directory.
        if (!libraryDir.exists() || !libraryDir.isDirectory()) {//Directory does not exist
            if (permissionFlag == ALLOW_ALL) {//All types are allowed to parse
                Slog.w(TAG, "No directory " + libraryDir + ", skipping");
            }
            return;
        }
        if (!libraryDir.canRead()) {//When the directory is unreadable, return directly
            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
            return;
        }

        // Iterate over the files in the directory and scan .xml files
        File platformFile = null;//Indicates the platform.xml file
        for (File f : libraryDir.listFiles()) {//traverse the files in the directory
            if (!f.isFile()) {//Filter content that is not a file
                continue;
            }

            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {//platform.xml is read last, skip first
                platformFile = f;
                continue;
            }

            if (!f.getPath().endsWith(".xml")) {//Only parse the .xml file
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {//The file is not allowed to be read, skip.
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }

            readPermissionsFromXml(f, permissionFlag);//Start parsing the xml file
        }

        // Read platform permissions last so it will take precedence
        if (platformFile != null) {
            readPermissionsFromXml(platformFile, permissionFlag);//Read platform.xml file
        }
    }

This method is mainly to check the permissions of the directory, and then traverse the files in the directory. The core method is to call the readPermissionsFromXml() method to read each node in the xml type file, such as: premission, features, library, etc., and then parse the The results are placed in the corresponding data structure of SystemConfig. The platform.xml file is read last.

private void readPermissionsFromXml(File permFile, int permissionFlag) {
        FileReader permReader = null;
        try {
            permReader = new FileReader(permFile);//Create a FileReader instance according to the path for reading files
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
            return;
        }
        Slog.i(TAG, "Reading permissions from " + permFile);

        final boolean lowRam = ActivityManager.isLowRamDeviceStatic();//Whether it is a low memory device

        try {
            XmlPullParser parser = Xml.newPullParser();//Used to parse xml type files
            parser.setInput(permReader);

            int type;
            while ((type=parser.next()) != parser.START_TAG
                        & amp; & amp; type != parser.END_DOCUMENT) {//Using the start and end of the file as the parsing range
                ;
            }

            if (type != parser. START_TAG) {
                throw new XmlPullParserException("No start tag found");
            }

            if (!parser.getName().equals("permissions") & amp; & amp; !parser.getName().equals("config")) {//Only parse the contents of permissions and config types
                throw new XmlPullParserException("Unexpected start tag in " + permFile
                         + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
            }

            final boolean allowAll = permissionFlag == ALLOW_ALL;// Whether to allow parsing of all types of nodes, which will be used below, the same below.
            final boolean allowLibs = (permissionFlag & amp; ALLOW_LIBS) != 0;//Whether to allow parsing of library type nodes
            final boolean allowFeatures = (permissionFlag & amp; ALLOW_FEATURES) != 0;//Whether to allow parsing feature type nodes
            final boolean allowPermissions = (permissionFlag & amp; ALLOW_PERMISSIONS) != 0;//Whether to allow parsing permission type nodes
            ?…
            while (true) {//Start parsing
                XmlUtils. nextElement(parser);
                if (parser. getEventType() == XmlPullParser. END_DOCUMENT) {
                    break;
                }

                String name = parser.getName();//Get node name
                if (name == null) {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
                switch (name) {
                    case "group": {//Analyze the content of the group type node in the xml file, the same below.
                        if (allowAll) {//Whether to allow parsing of all types of nodes
                            String gidStr = parser. getAttributeValue(null, "gid");
                            if (gidStr != null) {
                                int gid = android.os.Process.getGidForName(gidStr);
                                mGlobalGids = appendInt(mGlobalGids, gid);
                            } else {
                                Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at "
                                         + parser. getPositionDescription());
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "permission": {
                        if (allowPermissions) {
                            String perm = parser. getAttributeValue(null, "name");
                            if (perm == null) {
                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                         + parser. getPositionDescription());
                                XmlUtils.skipCurrentTag(parser);
                                break;
                            }
                            perm = perm. intern();
                            readPermission(parser, perm);
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    ?…
                    default: {
                        Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser. getPositionDescription());
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                }
            }
        } catch (XmlPullParserException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } catch (IOException e) {
            Slog.w(TAG, "Got exception parsing permissions.", e);
        } finally {
            IoUtils. closeQuietly(permReader);
        }

        // Some devices can be field-converted to FBE, so offer to splice in
        // those features if not already defined by the static config
        if (StorageManager. isFileEncryptedNativeOnly()) {
            addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);//You can add some additional features
            addFeature(PackageManager. FEATURE_SECURELY_REMOVES_USERS, 0);
        }

        ?…
    }