Android權限系統(一):開機獲取權限信息

一.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" />


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章