一.SystemConfig的整机权限信息

  Android在SystemConfig的构造函数中会通过读取相关的文件来加载整机的权限信息。这些文件是{partition}/etc/permissions下面的文件。其中{partition}指代的分区包含了/system,/vendor,/odm,/oem/,/product/,/system_ext等目录。{partition}/etc/permissions目录下的文件以xml的形式存在以方便解析。

frameworks/base/core/java/com/android/server/SystemConfig.java

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

        // 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);
        readPermissions(Environment.buildPath(
                Environment.getVendorDirectory(), "etc", "permissions"), 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);
        readPermissions(Environment.buildPath(
                Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);

        String skuProperty = SystemProperties.get(SKU_PROPERTY, "");
        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);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);

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

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

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

  解析的规则如下。选取一些常用的进行说明:
1.permission标签:定义了一个权限,name为权限的名称,子标签gid表示权限所属的gid,一个权限可以同时属于几个不同的gid;
2.assign-permission标签:授予某个uid某个特定权限;
3.split-permission标签:当系统授予某个应用某个split permission时,如果应用的targetsdk低于split-permission的targetsdk(没有指定的话targetsdk为10001)会同时授予new-permission子标签对应的权限;
4.feature标签:定义系统的特性(feature);
5.unavailable-feature标签:定义了需要移除的系统特性(feature);
6.system-user-whitelisted-app标签:主用户下强制启用的system app;
7.system-user-blacklisted-app标签:主用户下强制不启用的system app;
8.library标签:定义了应用的共享jar包;
9.privapp-permissions标签:为特权应用授予或拒绝权限。如果特权应用需要使用到级别signature|privileged的系统权限,必须显式地在在permission子标签下声明授予权限或者在deny-permission字标签下声明不授予权限,两者必选其一,不然会造成开不了机。参考特许权限白名单

frameworks/base/core/java/com/android/server/SystemConfig.java

private void readPermissionsFromXml(File permFile, int permissionFlag) {
        FileReader permReader = null;
        try {
            permReader = new FileReader(permFile);
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
            return;
        }

        final boolean lowRam = ActivityManager.isLowRamDeviceStatic();

        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(permReader);

            int type;
            while ((type=parser.next()) != parser.START_TAG
                       && type != parser.END_DOCUMENT) {
                ;
            }

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

            if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
                throw new XmlPullParserException("Unexpected start tag in " + permFile
                        + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
            }

            final boolean allowAll = permissionFlag == ALLOW_ALL;
            final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
            final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
            final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
            final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
            final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
                    != 0;
            final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
            final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
                    != 0;
            final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0;
            while (true) {
                XmlUtils.nextElement(parser);
                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
                    break;
                }

                String name = parser.getName();
                if (name == null) {
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }
                switch (name) {
                    case "group": {
                        if (allowAll) {
                            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;
                    case "assign-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;
                            }
                            String uidStr = parser.getAttributeValue(null, "uid");
                            if (uidStr == null) {
                                Slog.w(TAG, "<" + name + "> without uid in " + permFile
                                        + " at " + parser.getPositionDescription());
                                XmlUtils.skipCurrentTag(parser);
                                break;
                            }
                            int uid = Process.getUidForName(uidStr);
                            if (uid < 0) {
                                Slog.w(TAG, "<" + name + "> with unknown uid \""
                                        + uidStr + "  in " + permFile + " at "
                                        + parser.getPositionDescription());
                                XmlUtils.skipCurrentTag(parser);
                                break;
                            }
                            perm = perm.intern();
                            ArraySet<String> perms = mSystemPermissions.get(uid);
                            if (perms == null) {
                                perms = new ArraySet<String>();
                                mSystemPermissions.put(uid, perms);
                            }
                            perms.add(perm);
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "split-permission": {
                        if (allowPermissions) {
                            readSplitPermission(parser, permFile);
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    case "library": {
                        if (allowLibs) {
                            String lname = parser.getAttributeValue(null, "name");
                            String lfile = parser.getAttributeValue(null, "file");
                            String ldependency = parser.getAttributeValue(null, "dependency");
                            if (lname == null) {
                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                        + parser.getPositionDescription());
                            } else if (lfile == null) {
                                Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
                                        + parser.getPositionDescription());
                            } else {
                                //Log.i(TAG, "Got library " + lname + " in " + lfile);
                                SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
                                        ldependency == null ? new String[0] : ldependency.split(":"));
                                mSharedLibraries.put(lname, entry);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "feature": {
                        if (allowFeatures) {
                            String fname = parser.getAttributeValue(null, "name");
                            int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
                            boolean allowed;
                            if (!lowRam) {
                                allowed = true;
                            } else {
                                String notLowRam = parser.getAttributeValue(null, "notLowRam");
                                allowed = !"true".equals(notLowRam);
                            }
                            if (fname == null) {
                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
                                        + parser.getPositionDescription());
                            } else if (allowed) {
                                addFeature(fname, fversion);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "unavailable-feature": {
                        if (allowFeatures) {
                            String fname = parser.getAttributeValue(null, "name");
                            if (fname == null) {
                                Slog.w(TAG, "<" + name + "> without name in " + permFile
                                        + " at " + parser.getPositionDescription());
                            } else {
                                mUnavailableFeatures.add(fname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-in-power-save-except-idle": {
                        if (allowAll) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowInPowerSaveExceptIdle.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-in-power-save": {
                        if (allowAll) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowInPowerSave.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-in-data-usage-save": {
                        if (allowAll) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowInDataUsageSave.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-unthrottled-location": {
                        if (allowAll) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowUnthrottledLocation.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-ignore-location-settings": {
                        if (allowAll) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowIgnoreLocationSettings.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-implicit-broadcast": {
                        if (allowAll) {
                            String action = parser.getAttributeValue(null, "action");
                            if (action == null) {
                                Slog.w(TAG, "<" + name + "> without action in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mAllowImplicitBroadcasts.add(action);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "app-link": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in " + permFile
                                        + " at " + parser.getPositionDescription());
                            } else {
                                mLinkedApps.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "system-user-whitelisted-app": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mSystemUserWhitelistedApps.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "system-user-blacklisted-app": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mSystemUserBlacklistedApps.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "default-enabled-vr-app": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            String clsname = parser.getAttributeValue(null, "class");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else if (clsname == null) {
                                Slog.w(TAG, "<" + name + "> without class in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "backup-transport-whitelisted-service": {
                        if (allowFeatures) {
                            String serviceName = parser.getAttributeValue(null, "service");
                            if (serviceName == null) {
                                Slog.w(TAG, "<" + name + "> without service in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                ComponentName cn = ComponentName.unflattenFromString(serviceName);
                                if (cn == null) {
                                    Slog.w(TAG, "<" + name + "> with invalid service name "
                                            + serviceName + " in " + permFile
                                            + " at " + parser.getPositionDescription());
                                } else {
                                    mBackupTransportWhitelist.add(cn);
                                }
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "disabled-until-used-preinstalled-carrier-associated-app": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            String carrierPkgname = parser.getAttributeValue(null,
                                    "carrierAppPackage");
                            if (pkgname == null || carrierPkgname == null) {
                                Slog.w(TAG, "<" + name
                                        + "> without package or carrierAppPackage in " + permFile
                                        + " at " + parser.getPositionDescription());
                            } else {
                                List<String> associatedPkgs =
                                        mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
                                                carrierPkgname);
                                if (associatedPkgs == null) {
                                    associatedPkgs = new ArrayList<>();
                                    mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
                                            carrierPkgname, associatedPkgs);
                                }
                                associatedPkgs.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "disabled-until-used-preinstalled-carrier-app": {
                        if (allowAppConfigs) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG,
                                        "<" + name + "> without "
                                                + "package in " + permFile + " at "
                                                + parser.getPositionDescription());
                            } else {
                                mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "privapp-permissions": {
                        if (allowPrivappPermissions) {
                            // privapp permissions from system, vendor, product and system_ext
                            // partitions are stored separately. This is to prevent xml files in
                            // the vendor partition from granting permissions to priv apps in the
                            // system partition and vice versa.
                            boolean vendor = permFile.toPath().startsWith(
                                    Environment.getVendorDirectory().toPath() + "/")
                                    || permFile.toPath().startsWith(
                                    Environment.getOdmDirectory().toPath() + "/");
                            boolean product = permFile.toPath().startsWith(
                                    Environment.getProductDirectory().toPath() + "/");
                            boolean systemExt = permFile.toPath().startsWith(
                                    Environment.getSystemExtDirectory().toPath() + "/");
                            if (vendor) {
                                readPrivAppPermissions(parser, mVendorPrivAppPermissions,
                                        mVendorPrivAppDenyPermissions);
                            } else if (product) {
                                readPrivAppPermissions(parser, mProductPrivAppPermissions,
                                        mProductPrivAppDenyPermissions);
                            } else if (systemExt) {
                                readPrivAppPermissions(parser, mSystemExtPrivAppPermissions,
                                        mSystemExtPrivAppDenyPermissions);
                            } else {
                                readPrivAppPermissions(parser, mPrivAppPermissions,
                                        mPrivAppDenyPermissions);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    case "oem-permissions": {
                        if (allowOemPermissions) {
                            readOemPermissions(parser);
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    case "hidden-api-whitelisted-app": {
                        if (allowApiWhitelisting) {
                            String pkgname = parser.getAttributeValue(null, "package");
                            if (pkgname == null) {
                                Slog.w(TAG, "<" + name + "> without package in "
                                        + permFile + " at " + parser.getPositionDescription());
                            } else {
                                mHiddenApiPackageWhitelist.add(pkgname);
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "allow-association": {
                        if (allowAssociations) {
                            String target = parser.getAttributeValue(null, "target");
                            if (target == null) {
                                Slog.w(TAG, "<" + name + "> without target in " + permFile
                                        + " at " + parser.getPositionDescription());
                                XmlUtils.skipCurrentTag(parser);
                                break;
                            }
                            String allowed = parser.getAttributeValue(null, "allowed");
                            if (allowed == null) {
                                Slog.w(TAG, "<" + name + "> without allowed in " + permFile
                                        + " at " + parser.getPositionDescription());
                                XmlUtils.skipCurrentTag(parser);
                                break;
                            }
                            target = target.intern();
                            allowed = allowed.intern();
                            ArraySet<String> associations = mAllowedAssociations.get(target);
                            if (associations == null) {
                                associations = new ArraySet<>();
                                mAllowedAssociations.put(target, associations);
                            }
                            Slog.i(TAG, "Adding association: " + target + " <- " + allowed);
                            associations.add(allowed);
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    case "bugreport-whitelisted": {
                        String pkgname = parser.getAttributeValue(null, "package");
                        if (pkgname == null) {
                            Slog.w(TAG, "<" + name + "> without package in " + permFile
                                    + " at " + parser.getPositionDescription());
                        } else {
                            mBugreportWhitelistedPackages.add(pkgname);
                        }
                        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);
            addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
        }

        // Help legacy devices that may not have updated their static config
        if (StorageManager.hasAdoptable()) {
            addFeature(PackageManager.FEATURE_ADOPTABLE_STORAGE, 0);
        }

        if (ActivityManager.isLowRamDeviceStatic()) {
            addFeature(PackageManager.FEATURE_RAM_LOW, 0);
        } else {
            addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
        }

        for (String featureName : mUnavailableFeatures) {
            removeFeature(featureName);
        }
    }

Settings#readLPw和Settings#writeLPr

  在开机阶段,PMS扫描应用之前,PMS会调用Settings#readLPw来初始化权限信息。如果是首次开机,这个步骤会被跳过;如果不是首次开机,Settings#readLPw将会读取系统的/data/system/packages.xml和data/system/users/{userid}/runtime-permissions.xml来获取上次关机时刻保存的安装权限信息和运行时权限信息。

frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
        ...
                    mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
                    ...

  packages.xml和权限相关的节点主要有permissions节点,permission-trees节点还有package节点下的perms子节点。
  permissions节点形式如下所示。每一个item代表了一个定义的权限。name是权限的名称,package是定义这个权限的包名,protection是这个权限的安全级别。

<permissions>
        <item name="com.google.android.gms.auth.api.phone.permission.SEND" package="com.google.android.gms" protection="2" />
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />
        <item name="android.permission.ACCESS_CACHE_FILESYSTEM" package="android" protection="18" />
        ...
    </permissions>

  permission-trees节点形式如下所示。每一个item代表了一个定义的权限树。name是权限树的名称,package是定义这个权限树的包名。
  读取permissions节点的结果是更新Settings–>mPermissions(PermissionSettings)–>mPermissions(ArrayMap<String, BasePermission>)数据结构。读取permission-trees节点的结果是更新Settings–>mPermissions(PermissionSettings)–>mPermissionTrees(ArrayMap<String, BasePermission>)数据结构。

<permission-trees>
        <item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
    </permission-trees>

  package节点下的perms子节点如下。每一个Item表示和应用相关的安装权限(就是除了运行时权限以外的权限,运行时权限信息在 /data/system/users/{userid}/runtime-permissions.xml体现)信息。granted表示授权状态。flags表示这个权限的flag。

<package name="com.google.android.gms" codePath="/system/product/priv-app/GmsCore" nativeLibraryPath="/system/product/priv-app/GmsCore/lib" primaryCpuAbi="arm64-v8a" secondaryCpuAbi="armeabi-v7a" publicFlags="-1605714363" privateFlags="524296" ft="17290fd31e0" it="17290fd31e0" ut="17290fd31e0" version="201516037" sharedUserId="10153" isOrphaned="true">
        <sigs count="1" schemeVersion="3">
            <cert index="8" />
        </sigs>
        <perms>
            <item name="com.google.android.gms.auth.api.phone.permission.SEND" granted="true" flags="0" />
            <item name="android.permission.REAL_GET_TASKS" granted="true" flags="0" />
            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />
            <item name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" granted="true" flags="0" />
            <item name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" granted="true" flags="0" />
            <item name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" granted="true" flags="0" />

  读取package节点的结果是更新Settings–>mPackages(ArrayMap<String, PackageSetting>),插入应用包名为键,记录包信息的PackageSetting为值的记录。

frameworks/base/services/core/java/com/android/server/pm/Settings.java

private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
           ...
            } else if (userId > 0) {//独立UID应用,构建一个PackageSetting后记录到Settings-->mPackages
                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*/);
           ...
            } else if (sharedIdStr != null) {
                if (sharedUserId > 0) {//shareduserid应用,构建一个PackageSetting在解析packages.xml完成后记录到Settings-->mPackages,因为可能存在多个应用共用shareduserid,所以必须在解析packages.xml完成后才记录到Settings-->mPackages,方便在对应的SharedUserSetting记录所有共用这个shareduserid的应用
                    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);
              ...
                } else if (tagName.equals(TAG_PERMISSIONS)) {//解析package的权限信息
                    readInstallPermissionsLPr(parser,
                            packageSetting.getPermissionsState());
                    packageSetting.installPermissionsFixed = true;
               ...
    }

  前面提过packages.xml中package标签下的perms标签的每一项权限都是安装权限,根据其granted的值来预先设置权限状态。granted为true,使用grantInstallPermission授予安装权限并设置flags标签表示的flag;granted为false,使用revokeInstallPermission移除安装权限设置flags标签表示的flag。

frameworks/base/services/core/java/com/android/server/pm/Settings.java

void readInstallPermissionsLPr(XmlPullParser parser,
            PermissionsState permissionsState) throws IOException, XmlPullParserException {
        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 (tagName.equals(TAG_ITEM)) {
                String name = parser.getAttributeValue(null, ATTR_NAME);

                BasePermission bp = mPermissions.getPermission(name);
                if (bp == null) {
                    Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
                    XmlUtils.skipCurrentTag(parser);
                    continue;
                }

                String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
                final boolean granted = grantedStr == null
                        || Boolean.parseBoolean(grantedStr);

                String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
                final int flags = (flagsStr != null)
                        ? Integer.parseInt(flagsStr, 16) : 0;

                if (granted) {
                    if (permissionsState.grantInstallPermission(bp) ==
                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
                        XmlUtils.skipCurrentTag(parser);
                    } else {
                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
                    }
                } else {
                    if (permissionsState.revokeInstallPermission(bp) ==
                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
                        XmlUtils.skipCurrentTag(parser);
                    } else {
                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
                    }
                }
            } else {
                Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
                        + parser.getName());
                XmlUtils.skipCurrentTag(parser);
            }
        }
    }

  读取运行时权限信息代码入口:

frameworks/base/services/core/java/com/android/server/pm/Settings.java

boolean readLPw(@NonNull List<UserInfo> users) {
    ...
        for (UserInfo user : users) {
            mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
        }
        ...

  data/system/users/{userid}/runtime-permissions.xml部分内容如下。安装权限是对用所有用户都生效的,运行时权限时在不同用户下可能有不同的状态。
  pkg节点下item表示每一项和应用相关的运行时权限。name是运行时权限名字。granted表示授予状态。flags表示该运行时权限的flag。
  shared-user 节点下item表示该shareuserid下的通用运行时权限状态。name是运行时权限名字。granted表示授予状态。flags表示该运行时权限的flag。

<runtime-permissions version="8" fingerprint="OPPO/CPH1871/CPH1871:10/QKQ1.191008.001/1590481568:user/release-keys">
  <pkg name="com.coloros.backuprestore">
    <item name="android.permission.ACCESS_FINE_LOCATION" granted="false" flags="300" />
    <item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2380" />
    <item name="android.permission.ACCESS_COARSE_LOCATION" granted="false" flags="300" />
    <item name="android.permission.READ_PHONE_STATE" granted="false" flags="300" />
    <item name="android.permission.WRITE_CONTACTS" granted="false" flags="300" />
    <item name="android.permission.CAMERA" granted="false" flags="300" />
    <item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="false" flags="2300" />
    <item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="false" flags="2300" />
  </pkg>
   ...
 <shared-user name="android.uid.phone">
    <item name="android.permission.READ_SMS" granted="true" flags="3030" />
    <item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
    <item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="1030" />
    <item name="android.permission.RECEIVE_SMS" granted="true" flags="3030" />
    <item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
    <item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="1030" />
    <item name="android.permission.READ_PHONE_STATE" granted="true" flags="1030" />
    <item name="android.permission.SEND_SMS" granted="true" flags="3030" />
    <item name="android.permission.CALL_PHONE" granted="true" flags="30" />
    <item name="android.permission.WRITE_CONTACTS" granted="true" flags="1030" />
    <item name="android.permission.CAMERA" granted="true" flags="1030" />
    <item name="android.permission.WRITE_CALL_LOG" granted="true" flags="3030" />
    <item name="android.permission.USE_SIP" granted="true" flags="1030" />
    <item name="android.permission.PROCESS_OUTGOING_CALLS" granted="true" flags="3030" />
    <item name="android.permission.GET_ACCOUNTS" granted="true" flags="1030" />
    <item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
    <item name="android.permission.RECORD_AUDIO" granted="true" flags="1030" />
    <item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
    <item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="true" flags="3030" />
    <item name="com.android.voicemail.permission.ADD_VOICEMAIL" granted="true" flags="1030" />
  </shared-user>

  每次更新,授予和移除权限都会触发回调。

frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java

private PermissionCallback mPermissionCallback = new PermissionCallback() {
        @Override
        public void onGidsChanged(int appId, int userId) {
            mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
        }
        @Override
        public void onPermissionGranted(int uid, int userId) {
            mOnPermissionChangeListeners.onPermissionsChanged(uid);

            // Not critical; if this is lost, the application has to request again.
            synchronized (mPackages) {
                mSettings.writeRuntimePermissionsForUserLPr(userId, false);
            }
        }
        @Override
        public void onInstallPermissionGranted() {
            synchronized (mPackages) {
                scheduleWriteSettingsLocked();
            }
        }
        @Override
        public void onPermissionRevoked(int uid, int userId) {
            mOnPermissionChangeListeners.onPermissionsChanged(uid);

            synchronized (mPackages) {
                // Critical; after this call the application should never have the permission
                mSettings.writeRuntimePermissionsForUserLPr(userId, true);
            }

            final int appId = UserHandle.getAppId(uid);
            killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
        }
        @Override
        public void onInstallPermissionRevoked() {
            synchronized (mPackages) {
                scheduleWriteSettingsLocked();
            }
        }
        @Override
        public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
            synchronized (mPackages) {
                for (int userId : updatedUserIds) {
                    mSettings.writeRuntimePermissionsForUserLPr(userId, sync);
                }
            }
        }
        @Override
        public void onInstallPermissionUpdated() {
            synchronized (mPackages) {
                scheduleWriteSettingsLocked();
            }
        }
        @Override
        public void onPermissionRemoved() {
            synchronized (mPackages) {
                mSettings.writeLPr();
            }
        }
    };

  安装权限被更新flag时会回调onInstallPermissionUpdated,从而执行PMS#scheduleWriteSettingsLocked–>Settings#writeLPr,从而更新/data/system/packages.xml。
  而运行时权限更新flag时会回调onPermissionUpdated,从而执行Settings#writeRuntimePermissionsForUserLPr-来更新data/system/users/{userid}/runtime-permissions.xml。

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private void updatePermissionFlags(String permName, String packageName, int flagMask,
        ...
        if (permissionUpdated && callback != null) {
            // Install and runtime permissions are stored in different places,
            // so figure out what permission changed and persist the change.
            if (permissionsState.getInstallPermissionState(permName) != null) {//安装权限
                callback.onInstallPermissionUpdated();
            } else if (permissionsState.getRuntimePermissionState(permName, userId) != null//运行时权限
                    || hadState) {
                callback.onPermissionUpdated(new int[] { userId }, false);
            }
        }
    }

  此外,运行时权限在授予或者移除时会分别回调onPermissionGranted和onPermissionRevoked,这两个函数也会更新data/system/users/{userid}/runtime-permissions.xml。安装权限在授予或者移除时没有回调。onInstallPermissionGranted和onInstallPermissionRevoked是在安全级别包含development级别的权限被授予或者移除时触发的,与一般的安装权限无关。

PMS扫描应用权限信息

  PMS开机扫描各个应用时,会根据AndroidManifest,xml的内容获得应用需要的权限,应用定义的权限等信息,保存到特定的数据结构里面。常用的几个权限相关的标签说明:
  uses-permission标签:声明应用需要使用到的权限。将权限的名称保存到PackageParser.Package的requestedPermissions集合中。

frameworks/base/core/ava/android/content/pm/PackageParser.java

private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestUsesPermission);

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        String name = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestUsesPermission_name);

        int maxSdkVersion = 0;
        TypedValue val = sa.peekValue(
                com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
        if (val != null) {
            if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
                maxSdkVersion = val.data;
            }
        }

        final String requiredFeature = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);

        final String requiredNotfeature = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);

        sa.recycle();

        XmlUtils.skipCurrentTag(parser);

        if (name == null) {
            return true;
        }

        if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
            return true;
        }

        // Only allow requesting this permission if the platform supports the given feature.
        if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) {
            return true;
        }

        // Only allow requesting this permission if the platform doesn't support the given feature.
        if (requiredNotfeature != null && mCallback != null
                && mCallback.hasFeature(requiredNotfeature)) {
            return true;
        }

        int index = pkg.requestedPermissions.indexOf(name);
        if (index == -1) {
            pkg.requestedPermissions.add(name.intern());
        } else {
            Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
                    + name + " in package: " + pkg.packageName + " at: "
                    + parser.getPositionDescription());
        }

        return true;
    }

  permission标签:定义权限。name指定了权限的名称,protectionLevel指定了权限的保护等级,没有指定的话默认是Normal级别的。icon指定了权限图标。description 指明权限描述。label 指明权限标签。permissionGroup 为权限分组,方便将权限进行逻辑分组。backgroundPermission是对应的后台权限,name对应的则是前台权限,两者的关系后面会提到。permissionFlags指定了权限的flag。权限的信息保存在PermissionInfo中的结构中,后台权限和PermissionInfo保存在Permission中,所有Permission会被添加到PackageParser.Package的permissions集合中。

frameworks/base/core/ava/android/content/pm/PackageParser.java

private boolean parsePermission(Package owner, Resources res,
            XmlResourceParser parser, String[] outError)
        throws XmlPullParserException, IOException {

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestPermission);

        String backgroundPermission = null;
        if (sa.hasValue(
                com.android.internal.R.styleable.AndroidManifestPermission_backgroundPermission)) {
            if ("android".equals(owner.packageName)) {
                backgroundPermission = sa.getNonResourceString(
                        com.android.internal.R.styleable
                                .AndroidManifestPermission_backgroundPermission);
            } else {
                Slog.w(TAG, owner.packageName + " defines a background permission. Only the "
                        + "'android' package can do that.");
            }
        }

        Permission perm = new Permission(owner, backgroundPermission);
        if (!parsePackageItemInfo(owner, perm.info, outError,
                "<permission>", sa, true /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestPermission_name,
                com.android.internal.R.styleable.AndroidManifestPermission_label,
                com.android.internal.R.styleable.AndroidManifestPermission_icon,
                com.android.internal.R.styleable.AndroidManifestPermission_roundIcon,
                com.android.internal.R.styleable.AndroidManifestPermission_logo,
                com.android.internal.R.styleable.AndroidManifestPermission_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        perm.info.group = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
        if (perm.info.group != null) {
            perm.info.group = perm.info.group.intern();
        }

        perm.info.descriptionRes = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermission_description,
                0);

        perm.info.requestRes = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermission_request, 0);

        perm.info.protectionLevel = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
                PermissionInfo.PROTECTION_NORMAL);

        perm.info.flags = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);

        // For now only platform runtime permissions can be restricted
        if (!perm.info.isRuntime() || !"android".equals(perm.info.packageName)) {
            perm.info.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
            perm.info.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
        } else {
            // The platform does not get to specify conflicting permissions
            if ((perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
                    && (perm.info.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
                throw new IllegalStateException("Permission cannot be both soft and hard"
                        + " restricted: " + perm.info.name);
            }
        }

        sa.recycle();

        if (perm.info.protectionLevel == -1) {
            outError[0] = "<permission> does not specify protectionLevel";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);

        if (perm.info.getProtectionFlags() != 0) {
            if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
                    && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
                    PermissionInfo.PROTECTION_SIGNATURE) {
                outError[0] = "<permission>  protectionLevel specifies a non-instant flag but is "
                        + "not based on signature type";
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return false;
            }
        }

        if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.permissions.add(perm);

        return true;
    }

  permission-group标签:定义权限组。icon指定了权限组图标,label指定了权限组标签。description指定了权限组描述。request指定申请前台权限时显示的申请请求字符串。requestDetail指定了申请前台权限时的提示信息。backgroundRequest指定申请后台权限时显示的申请请求字符串。backgroundRequestDetail指定申请后台权限时的提示信息。目前使用了后台权限的前台权限有android.permission.ACCESS_FINE_LOCATION,android.permission.ACCESS_COARSE_LOCATION
这些位置相关权限。更多详细信息请参考Google官方说明:三态位置权限

frameworks/base/core/ava/android/content/pm/PackageParser.java

private boolean parsePermissionGroup(Package owner, int flags, Resources res,
            XmlResourceParser parser, String[] outError)
            throws XmlPullParserException, IOException {
        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup);

        int requestDetailResourceId = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
        int backgroundRequestResourceId = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
                0);
        int backgroundRequestDetailResourceId = sa.getResourceId(
                com.android.internal.R.styleable
                        .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);

        PermissionGroup perm = new PermissionGroup(owner, requestDetailResourceId,
                backgroundRequestResourceId, backgroundRequestDetailResourceId);

        if (!parsePackageItemInfo(owner, perm.info, outError,
                "<permission-group>", sa, true /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo,
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        perm.info.descriptionRes = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
                0);
        perm.info.requestRes = sa.getResourceId(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_request, 0);
        perm.info.flags = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
        perm.info.priority = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);

        sa.recycle();

        if (!parseAllMetaData(res, parser, "<permission-group>", perm,
                outError)) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.permissionGroups.add(perm);

        return true;
    }

  permission-tree标签:permission-tree标签主要用于使用PackageManager#addPermission动态添加权限。permission-tree本身不声明权限,只是声明了一组权限的命名空间。使用PackageManager#addPermission的方法是:
  .AndroidManifest.xml声明一个permission-tree,name的名称是必须的,是要动态添加的权限名称的一个子字符串。当要动态添加的权限在系统中没有记录时,调用PackageManager#addPermission的应用UID要和定义permission-tree的应用UID一致。
  举个例子,一个应用在其AndroidManifest.xml声明了name为“com.example.foo”的permission-tree的节点,那么,该应用可以自由地使用PackageManager#addPermission添加名字带有“com.example.foo”的任意权限,例如“com.example.foo.FOR_EXAMPLE”,而且这些要添加的权限不需要在系统或者本应用中使用permission标签显式定义,如果要添加的权限名称在系统中曾经被使用过,那么它一定要是使用动态方法添加的权限,不然会报错:“Not allowed to modify non-dynamic permission xxx”。
  解析AndroidManifest,xml的permission-tree节点:
frameworks/base/core/java/android/content/pm/PackageParser.java

private boolean parsePermissionTree(Package owner, Resources res,
            XmlResourceParser parser, String[] outError)
        throws XmlPullParserException, IOException {
        Permission perm = new Permission(owner, (String) null);

        TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestPermissionTree);

        if (!parsePackageItemInfo(owner, perm.info, outError,
                "<permission-tree>", sa, true /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_roundIcon,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_logo,
                com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) {
            sa.recycle();
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        sa.recycle();

        int index = perm.info.name.indexOf('.');
        if (index > 0) {
            index = perm.info.name.indexOf('.', index+1);
        }
        if (index < 0) {
            outError[0] = "<permission-tree> name has less than three segments: "
                + perm.info.name;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        perm.info.descriptionRes = 0;
        perm.info.requestRes = 0;
        perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
        perm.tree = true;

        if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
                outError)) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.permissions.add(perm);

        return true;
    }

  动态添加权限的实现:

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private boolean addDynamicPermission(
            PermissionInfo info, int callingUid, PermissionCallback callback) {
        if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
            throw new SecurityException("Instant apps can't add permissions");
        }
        if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
            throw new SecurityException("Label must be specified in permission");
        }
        final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);//查找要动态添加的权限在系统中是否有记录
        final boolean added;
        final boolean changed;
        synchronized (mLock) {
            BasePermission bp = mSettings.getPermissionLocked(info.name);
            added = bp == null;
            int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
            if (added) {//没有记录
                enforcePermissionCapLocked(info, tree);
                bp = new BasePermission(info.name, tree.getSourcePackageName(),
                        BasePermission.TYPE_DYNAMIC);
            } else if (!bp.isDynamic()) {//不是动态添加的记录就报错
                throw new SecurityException("Not allowed to modify non-dynamic permission "
                        + info.name);
            }
            changed = bp.addToTree(fixedLevel, info, tree);//设置新添加权限的字段
            if (added) {
                mSettings.putPermissionLocked(info.name, bp);//添加到系统记录
            }
        }
        if (changed && callback != null) {
            callback.onPermissionChanged();
        }
        return added;
    }

权限状态更新

  在包扫描的结尾阶段,都会有两步跟权限相关的操作:commitReconciledScanResultLocked和updateSettingsLI。
  commitReconciledScanResultLocked这一步会同步扫描得到的权限信息到PermissionManagerService,以向PermissionManagerService报告本次扫描的apk新增了哪些权限、权限树、权限组,然后让PermissionManagerService记录下来,这个在下章节Systemconfig/packages.xml/扫描包 权限区别会介绍到。
  updateSettingsLI这一步主要是根据扫描apk的结果改变apk的权限状态(PermissionsState)。

frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java

@GuardedBy("mPackages")
    private void commitPackagesLocked(final CommitRequest request) {
                ...
                commitReconciledScanResultLocked(reconciledPkg);//第一步
            updateSettingsLI(pkg, reconciledPkg.installArgs.installerPackageName, request.mAllUsers,
                    res, reconciledPkg.installArgs.user, reconciledPkg.installArgs.installReason);//第二步
                ...

  updateSettingsLI最终会调用到updateSettingsInternalLI,通过updatePermissions来更新权限状态。
frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java

private void updateSettingsInternalLI(PackageParser.Package pkg,
            String installerPackageName, int[] allUsers, int[] installedForUsers,
            PackageInstalledInfo res, UserHandle user, int installReason) {
        ...
        synchronized (mPackages) {
// NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions
            mPermissionManager.updatePermissions(pkg.packageName, pkg, true, mPackages.values(),
                    mPermissionCallback);
        ...

  最终会调用到PermissionManagerService#updatePermissions。该函数的第一个参数是要更新权限状态的包名,第二个参数是要更新权限状态的 PackageParser.Package,第三个参数是第二个参数对应的Volumn UUID,第四个参数的传入的flag,第五个参数是一个PackageParser.Package集合,一般是PMS记录的包集合,第六个参数是权限状态发生变化时的回调。
  flag参数会决定更新权限状态的策略。当带有UPDATE_PERMISSIONS_ALL标志位时,系统会尝试更新所有包的权限状态,这个通常发生在PermissionManagerService#updateAllPermissions调用时,在PackageManagerService.java构造函数扫描包动作结束后的一个点,PermissionManagerService#updateAllPermissions会被调用。当带有UPDATE_PERMISSIONS_REPLACE_PKG标志位时,系统会重置这个包的权限状态(PermissionSettings)再作修改,否则就会在原来的权限状态上作修改。UPDATE_PERMISSIONS_REPLACE_ALL标志位和UPDATE_PERMISSIONS_ALL搭配使用,表示既要更新所有包的权限状态,又要重置这些包的权限状态。

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private void updatePermissions(String changingPkgName, PackageParser.Package changingPkg,
            String replaceVolumeUuid, int flags, Collection<PackageParser.Package> allPackages,
            PermissionCallback callback) {
        // TODO: Most of the methods exposing BasePermission internals [source package name,
        // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
        // have package settings, we should make note of it elsewhere [map between
        // source package name and BasePermission] and cycle through that here. Then we
        // define a single method on BasePermission that takes a PackageSetting, changing
        // package name and a package.
        // NOTE: With this approach, we also don't need to tree trees differently than
        // normal permissions. Today, we need two separate loops because these BasePermission
        // objects are stored separately.
        // Make sure there are no dangling permission trees.
        flags = updatePermissionTrees(changingPkgName, changingPkg, flags);

        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        flags = updatePermissions(changingPkgName, changingPkg, flags);

        synchronized (mLock) {
            if (mBackgroundPermissions == null) {
                // Cache background -> foreground permission mapping.
                // Only system declares background permissions, hence mapping does never change.
                mBackgroundPermissions = new ArrayMap<>();
                for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
                    if (bp.perm != null && bp.perm.info != null
                            && bp.perm.info.backgroundPermission != null) {
                        String fgPerm = bp.name;
                        String bgPerm = bp.perm.info.backgroundPermission;

                        List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
                        if (fgPerms == null) {
                            fgPerms = new ArrayList<>();
                            mBackgroundPermissions.put(bgPerm, fgPerms);
                        }

                        fgPerms.add(fgPerm);
                    }
                }
            }
        }

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState");
        // Now update the permissions for all packages, in particular
        // replace the granted permissions of the system packages.
        if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {//带有UPDATE_PERMISSIONS_ALL表示要更新所有包的权限状态,先把入参changingPkg以外的包权限状态更新
            for (PackageParser.Package pkg : allPackages) {
                if (pkg != changingPkg) {
                    // Only replace for packages on requested volume
                    final String volumeUuid = getVolumeUuidForPackage(pkg);
                    final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
                            && Objects.equals(replaceVolumeUuid, volumeUuid);//带有UPDATE_PERMISSIONS_REPLACE_ALL表示当前处于安卓大版本升级后或者创建新用户的状态
                    restorePermissionState(pkg, replace, changingPkgName, callback);
                }
            }
        }

        if (changingPkg != null) {//单独更新入参changingPkg包的权限状态
            // Only replace for packages on requested volume
            final String volumeUuid = getVolumeUuidForPackage(changingPkg);
            final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
                    && Objects.equals(replaceVolumeUuid, volumeUuid);
            restorePermissionState(changingPkg, replace, changingPkgName, callback);
        }
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

  restorePermissionState是更新权限状态的关键,参考代码中的注释:

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,
            @Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
        // IMPORTANT: There are two types of permissions: install and runtime.
        // Install time permissions are granted when the app is installed to
        // all device users and users added in the future. Runtime permissions
        // are granted at runtime explicitly to specific users. Normal and signature
        // protected permissions are install time permissions. Dangerous permissions
        // are install permissions if the app's target SDK is Lollipop MR1 or older,
        // otherwise they are runtime permissions. This function does not manage
        // runtime permissions except for the case an app targeting Lollipop MR1
        // being upgraded to target a newer SDK, in which case dangerous permissions
        // are transformed from install time to runtime ones.

        final PackageSetting ps = (PackageSetting) pkg.mExtras;
        if (ps == null) {
            return;
        }

        final PermissionsState permissionsState = ps.getPermissionsState();
        PermissionsState origPermissions = permissionsState;

        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();

        boolean runtimePermissionsRevoked = false;
        int[] updatedUserIds = EMPTY_INT_ARRAY;

        boolean changedInstallPermission = false;
         //当带有UPDATE_PERMISSIONS_REPLACE_ALL或UPDATE_PERMISSIONS_REPLACE_PKG标志位时,replace为true。当包没有shareduserid时,重置权限状态。当包有shareduserid时,检测对应的SharedUserSetting的权限状态,如果发现SharedUserSetting授予了未被PermissionManagerService记录的权限(包括安装权限和运行时权限),则移除掉这些权限。
        if (replace) {
            ps.setInstallPermissionsFixed(false);
            if (!ps.isSharedUser()) {
                origPermissions = new PermissionsState(permissionsState);
                permissionsState.reset();
            } else {
                // We need to know only about runtime permission changes since the
                // calling code always writes the install permissions state but
                // the runtime ones are written only if changed. The only cases of
                // changed runtime permissions here are promotion of an install to
                // runtime and revocation of a runtime from a shared user.
                synchronized (mLock) {
                    updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
                            ps.getSharedUser(), UserManagerService.getInstance().getUserIds());
                    if (!ArrayUtils.isEmpty(updatedUserIds)) {
                        runtimePermissionsRevoked = true;
                    }
                }
            }
        }

        permissionsState.setGlobalGids(mGlobalGids);

        synchronized (mLock) {
            ArraySet<String> newImplicitPermissions = new ArraySet<>();

            final int N = pkg.requestedPermissions.size();
            for (int i = 0; i < N; i++) {
               //遍历包中每一个使用uses-permission声明的权限
                final String permName = pkg.requestedPermissions.get(i);
                final BasePermission bp = mSettings.getPermissionLocked(permName);
                final boolean appSupportsRuntimePermissions =
                        pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
                String upgradedActivityRecognitionPermission = null;

                if (DEBUG_INSTALL) {
                    Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
                }

                if (bp == null || bp.getSourcePackageSetting() == null) {
                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                        if (DEBUG_PERMISSIONS) {
                            Slog.i(TAG, "Unknown permission " + permName
                                    + " in package " + pkg.packageName);
                        }
                    }
                    continue;
                }

                // Cache newImplicitPermissions before modifing permissionsState as for the shared
                // uids the original and new state are the same object
                if (!origPermissions.hasRequestedPermission(permName)
                        && (pkg.implicitPermissions.contains(permName)
                                || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
                    if (pkg.implicitPermissions.contains(permName)) {
                        // If permName is an implicit permission, try to auto-grant
                        newImplicitPermissions.add(permName);

                        if (DEBUG_PERMISSIONS) {
                            Slog.i(TAG, permName + " is newly added for " + pkg.packageName);
                        }
                    } else {
                        // Special case for Activity Recognition permission. Even if AR permission
                        // is not an implicit permission we want to add it to the list (try to
                        // auto-grant it) if the app was installed on a device before AR permission
                        // was split, regardless of if the app now requests the new AR permission
                        // or has updated its target SDK and AR is no longer implicit to it.
                        // This is a compatibility workaround for apps when AR permission was
                        // split in Q.
                        final List<PermissionManager.SplitPermissionInfo> permissionList =
                                getSplitPermissions();
                        int numSplitPerms = permissionList.size();
                        for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
                            PermissionManager.SplitPermissionInfo sp =
                                    permissionList.get(splitPermNum);
                            String splitPermName = sp.getSplitPermission();
                            if (sp.getNewPermissions().contains(permName)
                                    && origPermissions.hasInstallPermission(splitPermName)) {
                                upgradedActivityRecognitionPermission = splitPermName;
                                newImplicitPermissions.add(permName);

                                if (DEBUG_PERMISSIONS) {
                                    Slog.i(TAG, permName + " is newly added for "
                                            + pkg.packageName);
                                }
                                break;
                            }
                        }
                    }
                }

                // Limit ephemeral apps to ephemeral allowed permissions.
                if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                    if (DEBUG_PERMISSIONS) {
                        Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
                                + " for package " + pkg.packageName);
                    }
                    continue;
                }

                if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
                    if (DEBUG_PERMISSIONS) {
                        Log.i(TAG, "Denying runtime-only permission " + bp.getName()
                                + " for package " + pkg.packageName);
                    }
                    continue;
                }

                final String perm = bp.getName();
                boolean allowedSig = false;
                int grant = GRANT_DENIED;

                // Keep track of app op permissions.
                if (bp.isAppOp()) {
                    mSettings.addAppOpPackage(perm, pkg.packageName);
                }

                if (bp.isNormal()) {
                    // For all apps normal permissions are install time ones.
                    //对于应用申请的普通级别的权限,是直接授权的(GRANT_INSTALL)
                    grant = GRANT_INSTALL;
                } else if (bp.isRuntime()) {//对于运行时权限,如果以前是安装权限的现在变更为运行时权限的,视作升级(GRANT_UPGRADE)的授权方式;如果是普通的运行时权限,视作运行时(GRANT_RUNTIME)授权方式
                    if (origPermissions.hasInstallPermission(bp.getName())
                            || upgradedActivityRecognitionPermission != null) {
                        // Before Q we represented some runtime permissions as install permissions,
                        // in Q we cannot do this anymore. Hence upgrade them all.
                        grant = GRANT_UPGRADE;
                    } else {
                        // For modern apps keep runtime permissions unchanged.
                        grant = GRANT_RUNTIME;
                    }
                } else if (bp.isSignature()) {//对于签名级别的权限,如果签名校对成功,可直接授权(GRANT_INSTALL)
                    // For all apps signature permissions are install time ones.
                    allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
                    if (allowedSig) {
                        grant = GRANT_INSTALL;
                    }
                }

                if (DEBUG_PERMISSIONS) {
                    Slog.i(TAG, "Considering granting permission " + perm + " to package "
                            + pkg.packageName);
                }

                if (grant != GRANT_DENIED) {
                    if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {//对于1.非系统应用;2.非重置权限状态下;3.目标更改权限不是运行时权限;4.目标更改权限是安装权限或者是签名权限但是签名不匹配时;5.目前包没有对目标权限授权 这5种情况同时满足的情形下,拒绝授权。例如部分厂商手机OTA时会对data分区部分app做升级,新的app声明了一些以前版本没有声明过的安装权限,那系统会拒绝为这个新app自动授予这个安装权限
                        // If this is an existing, non-system package, then
                        // we can't add any new permissions to it. Runtime
                        // permissions can be added any time - they ad dynamic.
                        if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
                            // Except...  if this is a permission that was added
                            // to the platform (note: need to only do this when
                            // updating the platform).
                            if (!isNewPlatformPermissionForPackage(perm, pkg)) {
                                grant = GRANT_DENIED;
                            }
                        }
                    }

                    switch (grant) {
                        case GRANT_INSTALL: {
                            // Revoke this as runtime permission to handle the case of
                            // a runtime permission being downgraded to an install one.
                            // Also in permission review mode we keep dangerous permissions
                            // for legacy apps
                            //运行时权限降级为安装权限的情况,移除掉这个运行时权限
                            for (int userId : UserManagerService.getInstance().getUserIds()) {
                                if (origPermissions.getRuntimePermissionState(
                                        perm, userId) != null) {
                                    // Revoke the runtime permission and clear the flags.
                                    origPermissions.revokeRuntimePermission(bp, userId);
                                    origPermissions.updatePermissionFlags(bp, userId,
                                            PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
                                    // If we revoked a permission permission, we have to write.
                                    updatedUserIds = ArrayUtils.appendInt(
                                            updatedUserIds, userId);
                                }
                            }
                            // Grant an install permission.
                            //正常情况下,直接授予安装权限
                            if (permissionsState.grantInstallPermission(bp) !=
                                    PERMISSION_OPERATION_FAILURE) {
                                changedInstallPermission = true;
                            }
                        } break;

                        case GRANT_RUNTIME: {
                            boolean hardRestricted = bp.isHardRestricted();
                            boolean softRestricted = bp.isSoftRestricted();

                            for (int userId : currentUserIds) {
                                // If permission policy is not ready we don't deal with restricted
                                // permissions as the policy may whitelist some permissions. Once
                                // the policy is initialized we would re-evaluate permissions.
                                //PermissionPolicyService是否已经初始化。PermissionPolicyService初始化后会针对限制权限作特殊处理,并且回调updateAllPermissions来重新设置权限状态。如果没有初始化,此处不必理会限制权限。
                                final boolean permissionPolicyInitialized =
                                        mPermissionPolicyInternal != null
                                                && mPermissionPolicyInternal.isInitialized(userId);

                                PermissionState permState = origPermissions
                                        .getRuntimePermissionState(perm, userId);
                                int flags = permState != null ? permState.getFlags() : 0;

                                boolean wasChanged = false;
                                 
                                 //限制免除         
                                boolean restrictionExempt =
                                        (origPermissions.getPermissionFlags(bp.name, userId)
                                                & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
                                //限制使用
                                boolean restrictionApplied = (origPermissions.getPermissionFlags(
                                        bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;

                                if (appSupportsRuntimePermissions) {//app支持运行时权限
                                    // If hard restricted we don't allow holding it
                                    if (permissionPolicyInitialized && hardRestricted) {//PermissionPolicyService已经初始化而且变成的权限时硬性限制权限的情况下
                                        if (!restrictionExempt) {
                                            if (permState != null && permState.isGranted()
                                                    && permissionsState.revokeRuntimePermission(
                                                    bp, userId) != PERMISSION_OPERATION_FAILURE) {//非限制免除情况下,移除掉已经授予过的运行时权限
                                                wasChanged = true;
                                            }
                                            if (!restrictionApplied) {//原来没有限制使用标志位的情况下,加上限制使用标志位
                                                flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                                wasChanged = true;
                                            }
                                        }
                                    // If soft restricted we allow holding in a restricted form
                                    } else if (permissionPolicyInitialized && softRestricted) {
                                        // Regardless if granted set the restriction flag as it
                                        // may affect app treatment based on this permission.
                                        if (!restrictionExempt && !restrictionApplied) {
                                            flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                            wasChanged = true;
                                        }
                                    }

                                    // Remove review flag as it is not necessary anymore
                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
                                        wasChanged = true;
                                    }

                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                                        wasChanged = true;
                                    // Hard restricted permissions cannot be held.
                                    } else if (!permissionPolicyInitialized
                                            || (!hardRestricted || restrictionExempt)) {
                                        if (permState != null && permState.isGranted()) {
                                            if (permissionsState.grantRuntimePermission(bp, userId)
                                                    == PERMISSION_OPERATION_FAILURE) {//如果1.PermissionPolicyService未被初始化;2.目标权限不是硬性限制或者可以限制免除(影响限制权限在非限制免除的情况下不能被豁免) ;两种情况满足之一,且原来的权限状态是授予这个运行时权限的,新的权限状态也应该授予
                                                wasChanged = true;
                                            }
                                        }
                                    }
                                } else {
                                    if (permState == null) {
                                        // New permission
                                        if (PLATFORM_PACKAGE_NAME.equals(
                                                bp.getSourcePackageName())) {
                                            if (!bp.isRemoved()) {
                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED
                                                        | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                                                wasChanged = true;
                                            }
                                        }
                                    }

                                    if (!permissionsState.hasRuntimePermission(bp.name, userId)
                                            && permissionsState.grantRuntimePermission(bp, userId)
                                                    != PERMISSION_OPERATION_FAILURE) {
                                        wasChanged = true;
                                    }

                                    // If legacy app always grant the permission but if restricted
                                    // and not exempt take a note a restriction should be applied.
                                    if (permissionPolicyInitialized
                                            && (hardRestricted || softRestricted)
                                                    && !restrictionExempt && !restrictionApplied) {
                                        flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                        wasChanged = true;
                                    }
                                }

                                // If unrestricted or restriction exempt, don't apply restriction.
                                //如果PermissionPolicyService已经初始化,目标权限不是限制权限或者限制免除的情况下,移除掉目标权限的FLAG_PERMISSION_APPLY_RESTRICTION标志位(如果有的话)
                                if (permissionPolicyInitialized) {
                                    if (!(hardRestricted || softRestricted) || restrictionExempt) {
                                        if (restrictionApplied) {
                                            flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
                                            // Dropping restriction on a legacy app implies a review
                                            if (!appSupportsRuntimePermissions) {
                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
                                            }
                                            wasChanged = true;
                                        }
                                    }
                                }

                                if (wasChanged) {
                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                                }

                                permissionsState.updatePermissionFlags(bp, userId,
                                        MASK_PERMISSION_FLAGS_ALL, flags);
                            }
                        } break;

                        case GRANT_UPGRADE: {
                            // Upgrade from Pre-Q to Q permission model. Make all permissions
                            // runtime
                            PermissionState permState = origPermissions
                                    .getInstallPermissionState(perm);
                            int flags = (permState != null) ? permState.getFlags() : 0;

                            BasePermission bpToRevoke =
                                    upgradedActivityRecognitionPermission == null
                                    ? bp : mSettings.getPermissionLocked(
                                            upgradedActivityRecognitionPermission);
                            // Remove install permission
                            //安装权限升级运行时权限,先把安装权限移除掉
                            if (origPermissions.revokeInstallPermission(bpToRevoke)
                                    != PERMISSION_OPERATION_FAILURE) {
                                origPermissions.updatePermissionFlags(bpToRevoke,
                                        UserHandle.USER_ALL,
                                        (MASK_PERMISSION_FLAGS_ALL
                                                & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
                                changedInstallPermission = true;
                            }
    
                            boolean hardRestricted = bp.isHardRestricted();
                            boolean softRestricted = bp.isSoftRestricted();

                            for (int userId : currentUserIds) {
                                // If permission policy is not ready we don't deal with restricted
                                // permissions as the policy may whitelist some permissions. Once
                                // the policy is initialized we would re-evaluate permissions.
                                final boolean permissionPolicyInitialized =
                                        mPermissionPolicyInternal != null
                                                && mPermissionPolicyInternal.isInitialized(userId);

                                boolean wasChanged = false;

                                boolean restrictionExempt =
                                        (origPermissions.getPermissionFlags(bp.name, userId)
                                                & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
                                boolean restrictionApplied = (origPermissions.getPermissionFlags(
                                        bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;

                                if (appSupportsRuntimePermissions) {
                                    // If hard restricted we don't allow holding it
                                    if (permissionPolicyInitialized && hardRestricted) {
                                        if (!restrictionExempt) {
                                            if (permState != null && permState.isGranted()
                                                    && permissionsState.revokeRuntimePermission(
                                                    bp, userId) != PERMISSION_OPERATION_FAILURE) {
                                                wasChanged = true;
                                            }
                                            if (!restrictionApplied) {
                                                flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                                wasChanged = true;
                                            }
                                        }
                                    // If soft restricted we allow holding in a restricted form
                                    } else if (permissionPolicyInitialized && softRestricted) {
                                        // Regardless if granted set the  restriction flag as it
                                        // may affect app treatment based on this permission.
                                        if (!restrictionExempt && !restrictionApplied) {
                                            flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                            wasChanged = true;
                                        }
                                    }

                                    // Remove review flag as it is not necessary anymore
                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
                                        wasChanged = true;
                                    }

                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
                                        wasChanged = true;
                                    // Hard restricted permissions cannot be held.
                                    } else if (!permissionPolicyInitialized ||
                                            (!hardRestricted || restrictionExempt)) {
                                        if (permissionsState.grantRuntimePermission(bp, userId) !=
                                                PERMISSION_OPERATION_FAILURE) {
                                             wasChanged = true;
                                        }
                                    }
                                } else {
                                    if (!permissionsState.hasRuntimePermission(bp.name, userId)
                                            && permissionsState.grantRuntimePermission(bp,
                                                    userId) != PERMISSION_OPERATION_FAILURE) {
                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
                                        wasChanged = true;
                                    }

                                    // If legacy app always grant the permission but if restricted
                                    // and not exempt take a note a restriction should be applied.
                                    if (permissionPolicyInitialized
                                            && (hardRestricted || softRestricted)
                                                    && !restrictionExempt && !restrictionApplied) {
                                        flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
                                        wasChanged = true;
                                    }
                                }

                                // If unrestricted or restriction exempt, don't apply restriction.
                                if (permissionPolicyInitialized) {
                                    if (!(hardRestricted || softRestricted) || restrictionExempt) {
                                        if (restrictionApplied) {
                                            flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
                                            // Dropping restriction on a legacy app implies a review
                                            if (!appSupportsRuntimePermissions) {
                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
                                            }
                                            wasChanged = true;
                                        }
                                    }
                                }

                                if (wasChanged) {
                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
                                }

                                permissionsState.updatePermissionFlags(bp, userId,
                                        MASK_PERMISSION_FLAGS_ALL, flags);
                            }
                        } break;

                        default: {
                            if (packageOfInterest == null
                                    || packageOfInterest.equals(pkg.packageName)) {
                                if (DEBUG_PERMISSIONS) {
                                    Slog.i(TAG, "Not granting permission " + perm
                                            + " to package " + pkg.packageName
                                            + " because it was previously installed without");
                                }
                            }
                        } break;
                    }
                } else {//结果是拒绝授权的情况下,移除掉目标权限
                    if (permissionsState.revokeInstallPermission(bp) !=
                            PERMISSION_OPERATION_FAILURE) {
                        // Also drop the permission flags.
                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                MASK_PERMISSION_FLAGS_ALL, 0);
                        changedInstallPermission = true;
                        Slog.i(TAG, "Un-granting permission " + perm
                                + " from package " + pkg.packageName
                                + " (protectionLevel=" + bp.getProtectionLevel()
                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                                + ")");
                    } else if (bp.isAppOp()) {
                        // Don't print warning for app op permissions, since it is fine for them
                        // not to be granted, there is a UI for the user to decide.
                        if (DEBUG_PERMISSIONS
                                && (packageOfInterest == null
                                        || packageOfInterest.equals(pkg.packageName))) {
                            Slog.i(TAG, "Not granting permission " + perm
                                    + " to package " + pkg.packageName
                                    + " (protectionLevel=" + bp.getProtectionLevel()
                                    + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                                    + ")");
                        }
                    }
                }
            }

            if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
                    !ps.isSystem() || ps.isUpdatedSystem()) {
                // This is the first that we have heard about this package, so the
                // permissions we have now selected are fixed until explicitly
                // changed.
                ps.setInstallPermissionsFixed(true);
            }

            updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
                    updatedUserIds);
            updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
                    permissionsState, pkg, newImplicitPermissions, updatedUserIds);
            updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, updatedUserIds);
        }

        // Persist the runtime permissions state for users with changes. If permissions
        // were revoked because no app in the shared user declares them we have to
        // write synchronously to avoid losing runtime permissions state.
        if (callback != null) {
            callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
        }

        for (int userId : updatedUserIds) {
            notifyRuntimePermissionStateChanged(pkg.packageName, userId);
        }
    }

Systemconfig/packages.xml/扫描包 权限区别

  Systemconfig能读取权限,packages.xml能读取权限,扫描包也可以读取权限,那这些权限有什么区别呢?
  Systemconfig定义的权限都是属于android这个包的,也就是系统权限,能够设置的权限属性比较少,常见的是通过group子标签设置一个gid。最终会记录到PermissionManagerService的mSettings(PermissionSettings)中。此外,SystemConfig不仅仅为权限服务,还有很多全局的系统配置都在里面实现。

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

PermissionManagerService(Context context,
            @NonNull Object externalLock) {
        mContext = context;
        mLock = externalLock;
        mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
        mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
        mSettings = new PermissionSettings(mLock);

        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        Watchdog.getInstance().addThread(mHandler);

        mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
                context, mHandlerThread.getLooper(), this);
        SystemConfig systemConfig = SystemConfig.getInstance();
        mSystemPermissions = systemConfig.getSystemPermissions();
        mGlobalGids = systemConfig.getGlobalGids();

        // propagate permission configuration
        final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
                SystemConfig.getInstance().getPermissions();
        synchronized (mLock) {
            for (int i=0; i<permConfig.size(); i++) {
                final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
                BasePermission bp = mSettings.getPermissionLocked(perm.name);
                if (bp == null) {
                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                    mSettings.putPermissionLocked(perm.name, bp);//记录来自SystemConifg的权限
                }
                if (perm.gids != null) {
                    bp.setGids(perm.gids, perm.perUser);
                }
            }
        }

        PermissionManagerServiceInternalImpl localService =
                new PermissionManagerServiceInternalImpl();
        LocalServices.addService(PermissionManagerServiceInternal.class, localService);
        LocalServices.addService(PermissionManagerInternal.class, localService);
    }

  而读取packages.xml的权限信息则是直接同步权限和权限树信息到PermissionManagerService的mSettings(PermissionSettings)中。packages.xml的权限信息来源于Systemconfig和扫描包得到的权限信息,本身不修改任何权限信息,只是帮助系统将权限记录(PermissionSettings)和状态(PermissionsState)初始化。
  如下所示,解析出来的权限信息会加入到Settings的mPermissions(PermissionSettings)中。

frameworks/base/services/core/java/com/android/server/pm/Settings.java

boolean readLPw(@NonNull List<UserInfo> users) {
               ...
                } else if (tagName.equals("permissions")) {
                    mPermissions.readPermissions(parser);

  Settings的mPermissions和PermissionManagerService的mSettings是同一个,见下面代码。
  此外,packages.xml还提供了记录应用权限状态(PermissionState)的记录&恢复功能,
shareuserid状态(SharedUserSetting)的记录&恢复功能。

frameworks/base/services/core/ava/com/android/server/pm/PackageManagerService.java

public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
        ...
        // 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);
            mComponentResolver = new ComponentResolver(sUserManager,
                    LocalServices.getService(PackageManagerInternal.class),
                    mPackages);
            mPermissionManager = PermissionManagerService.create(context,
                    mPackages /*externalLock*/);
            mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
            mSettings = new Settings(Environment.getDataDirectory(),
                    mPermissionManager.getPermissionSettings(), mPackages);//Settings的mPermissions和PermissionManagerService的mSettings是同一个
        }

  扫描包得到的权限和权限树信息是记录在PackageParser.Package的permissions(ArrayList< Permission>)中,权限组信息是记录在PackageParser.Package的permissionGroups(ArrayList< PermissionGroup>)中。在每次扫描包的最后阶段(PMS#commitPackageSettings),系统会通过PMS#addAllPermissionGroups和PMS#addAllPermissions将PackageParser.Package包含的权限组和权限信息同步到PermissionManagerService的mSettings(PermissionSettings)中。

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
        final int N = pkg.permissions.size();
        for (int i=0; i<N; i++) {
            PackageParser.Permission p = pkg.permissions.get(i);

            // Assume by default that we did not install this permission into the system.
            p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;

            synchronized (PermissionManagerService.this.mLock) {
                // Now that permission groups have a special meaning, we ignore permission
                // groups for legacy apps to prevent unexpected behavior. In particular,
                // permissions for one app being granted to someone just because they happen
                // to be in a group defined by another app (before this had no implications).
                if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
                    p.group = mSettings.mPermissionGroups.get(p.info.group);
                    // Warn for a permission in an unknown group.
                    if (DEBUG_PERMISSIONS
                            && p.info.group != null && p.group == null) {
                        Slog.i(TAG, "Permission " + p.info.name + " from package "
                                + p.info.packageName + " in an unknown group " + p.info.group);
                    }
                }

                if (p.tree) {
                    final BasePermission bp = BasePermission.createOrUpdate(
                            mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
                            mSettings.getAllPermissionTreesLocked(), chatty);
                    mSettings.putPermissionTreeLocked(p.info.name, bp);
                } else {
                    final BasePermission bp = BasePermission.createOrUpdate(
                            mSettings.getPermissionLocked(p.info.name),
                            p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
                    mSettings.putPermissionLocked(p.info.name, bp);
                }
            }
        }
    }

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) {
        final int N = pkg.permissionGroups.size();
        StringBuilder r = null;
        for (int i=0; i<N; i++) {
            final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
            final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name);
            final String curPackageName = (cur == null) ? null : cur.info.packageName;
            final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
            if (cur == null || isPackageUpdate) {
                mSettings.mPermissionGroups.put(pg.info.name, pg);
                if (chatty && DEBUG_PACKAGE_SCANNING) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    if (isPackageUpdate) {
                        r.append("UPD:");
                    }
                    r.append(pg.info.name);
                }
            } else {
                Slog.w(TAG, "Permission group " + pg.info.name + " from package "
                        + pg.info.packageName + " ignored: original from "
                        + cur.info.packageName);
                if (chatty && DEBUG_PACKAGE_SCANNING) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    r.append("DUP:");
                    r.append(pg.info.name);
                }
            }
        }
        if (r != null && DEBUG_PACKAGE_SCANNING) {
            Log.d(TAG, "  Permission Groups: " + r);
        }

    }

  相比Systemconfig,扫描包定义的权限可以包含更多的信息。例如可以定义所属权限组,图标,标签,描述,后台权限,安全等级等。以下是系统的AndroidManifest.xml定义的一个权限:

frameworks/base/core/res/AndroidManifest.xml

<permission android:name="android.permission.ACCESS_FINE_LOCATION"
        android:permissionGroup="android.permission-group.UNDEFINED"
        android:label="@string/permlab_accessFineLocation"
        android:description="@string/permdesc_accessFineLocation"
        android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
        android:protectionLevel="dangerous|instant" />