Loading core/java/com/android/server/SystemConfig.java +20 −0 Original line number Diff line number Diff line Loading @@ -339,6 +339,8 @@ public class SystemConfig { // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>(); // A set of package names that are allowed to use <install-constraints> manifest tag. private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>(); private String mModulesInstallerPackageName; Loading Loading @@ -535,6 +537,10 @@ public class SystemConfig { return mAllowedVendorApexes; } public Set<String> getInstallConstraintsAllowlist() { return mInstallConstraintsAllowlist; } public String getModulesInstallerPackageName() { return mModulesInstallerPackageName; } Loading Loading @@ -1455,6 +1461,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; case "install-constraints-allowed": { if (allowAppConfigs) { String packageName = parser.getAttributeValue(null, "package"); if (packageName == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mInstallConstraintsAllowlist.add(packageName); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); Loading core/res/res/values/attrs_manifest.xml +15 −0 Original line number Diff line number Diff line Loading @@ -3602,4 +3602,19 @@ false, the application cannot be profiled at all. Defaults to true. --> <attr name="enabled" format="boolean" /> </declare-styleable> <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by its child elements is true. It is possible to have multiple <code>install-constraints</code> tags in a single manifest, where each tag is evaluated independently. @hide --> <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" /> <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint starts with the given prefix. @hide --> <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix" parent="AndroidManifestInstallConstraints"> <attr name="value" /> </declare-styleable> </resources> services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.pkg.component; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Build; import android.util.ArraySet; import com.android.internal.R; import com.android.server.SystemConfig; import com.android.server.pm.pkg.parsing.ParsingPackage; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Set; /** * Utility methods for handling the tag {@code <install-constraints/>} * * @hide */ public class InstallConstraintsTagParser { private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix"; /** * @hide */ public static ParseResult<ParsingPackage> parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws XmlPullParserException, IOException { Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); if (!allowlist.contains(pkg.getPackageName())) { return input.skip("install-constraints cannot be used by this package"); } ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser); if (prefixes.isSuccess()) { if (validateFingerprintPrefixes(prefixes.getResult())) { return input.success(pkg); } else { return input.skip( "Install of this package is restricted on this device; device fingerprint" + " does not start with one of the allowed prefixes"); } } return input.skip(prefixes.getErrorMessage()); } private static ParseResult<Set<String>> parseFingerprintPrefixes( ParseInput input, Resources res, XmlResourceParser parser) throws XmlPullParserException, IOException { Set<String> prefixes = new ArraySet<>(); int type; while (true) { // move to the tag that contains the next prefix type = parser.next(); if (type == XmlPullParser.END_TAG) { if (prefixes.size() == 0) { return input.error("install-constraints must contain at least one constraint"); } return input.success(prefixes); } else if (type == XmlPullParser.START_TAG) { if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) { ParseResult<String> parsedPrefix = readFingerprintPrefixValue(input, res, parser); if (parsedPrefix.isSuccess()) { prefixes.add(parsedPrefix.getResult()); } else { return input.error(parsedPrefix.getErrorMessage()); } } else { return input.error("Unexpected tag: " + parser.getName()); } // consume the end tag of this attribute type = parser.next(); if (type != XmlPullParser.END_TAG) { return input.error("Expected end tag; instead got " + type); } } } } private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res, XmlResourceParser parser) { TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix); try { String value = sa.getString( R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value); if (value == null) { return input.error("Failed to specify prefix value"); } return input.success(value); } finally { sa.recycle(); } } private static boolean validateFingerprintPrefixes(Set<String> prefixes) { String fingerprint = Build.FINGERPRINT; for (String prefix : prefixes) { if (fingerprint.startsWith(prefix)) { return true; } } return false; } } services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +14 −4 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import com.android.server.pm.SharedUidMigration; import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ComponentParseUtils; import com.android.server.pm.pkg.component.InstallConstraintsTagParser; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityUtils; import com.android.server.pm.pkg.component.ParsedApexSystemService; Loading Loading @@ -169,9 +170,11 @@ public class ParsingPackageUtils { public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; public static final String TAG_APPLICATION = "application"; public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; public static final String TAG_EAT_COMMENT = "eat-comment"; public static final String TAG_FEATURE_GROUP = "feature-group"; public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints"; public static final String TAG_INSTRUMENTATION = "instrumentation"; public static final String TAG_KEY_SETS = "key-sets"; public static final String TAG_MANIFEST = "manifest"; Loading @@ -179,15 +182,16 @@ public class ParsingPackageUtils { public static final String TAG_OVERLAY = "overlay"; public static final String TAG_PACKAGE = "package"; public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_PERMISSION = "permission"; public static final String TAG_PERMISSION_GROUP = "permission-group"; public static final String TAG_PERMISSION_TREE = "permission-tree"; public static final String TAG_PROFILEABLE = "profileable"; public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; public static final String TAG_QUERIES = "queries"; public static final String TAG_RECEIVER = "receiver"; public static final String TAG_RESTRICT_UPDATE = "restrict-update"; public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_SUPPORTS_INPUT = "supports-input"; public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_USES_CONFIGURATION = "uses-configuration"; public static final String TAG_USES_FEATURE = "uses-feature"; public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; Loading @@ -196,8 +200,6 @@ public class ParsingPackageUtils { public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; public static final String TAG_USES_SDK = "uses-sdk"; public static final String TAG_USES_SPLIT = "uses-split"; public static final String TAG_PROFILEABLE = "profileable"; public static final String TAG_RECEIVER = "receiver"; public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes"; Loading Loading @@ -1040,6 +1042,8 @@ public class ParsingPackageUtils { return input.success(pkg); case TAG_RESTRICT_UPDATE: return parseRestrictUpdateHash(flags, input, pkg, res, parser); case TAG_INSTALL_CONSTRAINTS: return parseInstallConstraints(input, pkg, res, parser); case TAG_QUERIES: return parseQueries(input, pkg, res, parser); default: Loading Loading @@ -1729,6 +1733,12 @@ public class ParsingPackageUtils { return input.success(pkg); } private static ParseResult<ParsingPackage> parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); } private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); Loading services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +53 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,59 @@ public class SystemConfigTest { assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } /** * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. */ @Test public void readPermissions_installConstraints_successful() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getInstallConstraintsAllowlist()) .containsExactly("com.android.apex1"); } /** * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. */ @Test public void readPermissions_installConstraints_noPackage() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed/>\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); } /** * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed} * without {@link SystemConfig#ALLOW_VENDOR_APEX}. */ @Test public void readPermissions_installConstraints_noAppConfigs() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); } @Test public void readApexPrivAppPermissions_addAllPermissions() throws Exception { Loading Loading
core/java/com/android/server/SystemConfig.java +20 −0 Original line number Diff line number Diff line Loading @@ -339,6 +339,8 @@ public class SystemConfig { // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>(); // A set of package names that are allowed to use <install-constraints> manifest tag. private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>(); private String mModulesInstallerPackageName; Loading Loading @@ -535,6 +537,10 @@ public class SystemConfig { return mAllowedVendorApexes; } public Set<String> getInstallConstraintsAllowlist() { return mInstallConstraintsAllowlist; } public String getModulesInstallerPackageName() { return mModulesInstallerPackageName; } Loading Loading @@ -1455,6 +1461,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; case "install-constraints-allowed": { if (allowAppConfigs) { String packageName = parser.getAttributeValue(null, "package"); if (packageName == null) { Slog.w(TAG, "<" + name + "> without package in " + permFile + " at " + parser.getPositionDescription()); } else { mInstallConstraintsAllowlist.add(packageName); } } else { logNotAllowedInPartition(name, permFile, parser); } XmlUtils.skipCurrentTag(parser); } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); Loading
core/res/res/values/attrs_manifest.xml +15 −0 Original line number Diff line number Diff line Loading @@ -3602,4 +3602,19 @@ false, the application cannot be profiled at all. Defaults to true. --> <attr name="enabled" format="boolean" /> </declare-styleable> <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by its child elements is true. It is possible to have multiple <code>install-constraints</code> tags in a single manifest, where each tag is evaluated independently. @hide --> <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" /> <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint starts with the given prefix. @hide --> <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix" parent="AndroidManifestInstallConstraints"> <attr name="value" /> </declare-styleable> </resources>
services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java 0 → 100644 +131 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.pkg.component; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseResult; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Build; import android.util.ArraySet; import com.android.internal.R; import com.android.server.SystemConfig; import com.android.server.pm.pkg.parsing.ParsingPackage; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Set; /** * Utility methods for handling the tag {@code <install-constraints/>} * * @hide */ public class InstallConstraintsTagParser { private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix"; /** * @hide */ public static ParseResult<ParsingPackage> parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws XmlPullParserException, IOException { Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); if (!allowlist.contains(pkg.getPackageName())) { return input.skip("install-constraints cannot be used by this package"); } ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser); if (prefixes.isSuccess()) { if (validateFingerprintPrefixes(prefixes.getResult())) { return input.success(pkg); } else { return input.skip( "Install of this package is restricted on this device; device fingerprint" + " does not start with one of the allowed prefixes"); } } return input.skip(prefixes.getErrorMessage()); } private static ParseResult<Set<String>> parseFingerprintPrefixes( ParseInput input, Resources res, XmlResourceParser parser) throws XmlPullParserException, IOException { Set<String> prefixes = new ArraySet<>(); int type; while (true) { // move to the tag that contains the next prefix type = parser.next(); if (type == XmlPullParser.END_TAG) { if (prefixes.size() == 0) { return input.error("install-constraints must contain at least one constraint"); } return input.success(prefixes); } else if (type == XmlPullParser.START_TAG) { if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) { ParseResult<String> parsedPrefix = readFingerprintPrefixValue(input, res, parser); if (parsedPrefix.isSuccess()) { prefixes.add(parsedPrefix.getResult()); } else { return input.error(parsedPrefix.getErrorMessage()); } } else { return input.error("Unexpected tag: " + parser.getName()); } // consume the end tag of this attribute type = parser.next(); if (type != XmlPullParser.END_TAG) { return input.error("Expected end tag; instead got " + type); } } } } private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res, XmlResourceParser parser) { TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix); try { String value = sa.getString( R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value); if (value == null) { return input.error("Failed to specify prefix value"); } return input.success(value); } finally { sa.recycle(); } } private static boolean validateFingerprintPrefixes(Set<String> prefixes) { String fingerprint = Build.FINGERPRINT; for (String prefix : prefixes) { if (fingerprint.startsWith(prefix)) { return true; } } return false; } }
services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +14 −4 Original line number Diff line number Diff line Loading @@ -95,6 +95,7 @@ import com.android.server.pm.SharedUidMigration; import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ComponentParseUtils; import com.android.server.pm.pkg.component.InstallConstraintsTagParser; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityUtils; import com.android.server.pm.pkg.component.ParsedApexSystemService; Loading Loading @@ -169,9 +170,11 @@ public class ParsingPackageUtils { public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; public static final String TAG_APPLICATION = "application"; public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; public static final String TAG_EAT_COMMENT = "eat-comment"; public static final String TAG_FEATURE_GROUP = "feature-group"; public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints"; public static final String TAG_INSTRUMENTATION = "instrumentation"; public static final String TAG_KEY_SETS = "key-sets"; public static final String TAG_MANIFEST = "manifest"; Loading @@ -179,15 +182,16 @@ public class ParsingPackageUtils { public static final String TAG_OVERLAY = "overlay"; public static final String TAG_PACKAGE = "package"; public static final String TAG_PACKAGE_VERIFIER = "package-verifier"; public static final String TAG_ATTRIBUTION = "attribution"; public static final String TAG_PERMISSION = "permission"; public static final String TAG_PERMISSION_GROUP = "permission-group"; public static final String TAG_PERMISSION_TREE = "permission-tree"; public static final String TAG_PROFILEABLE = "profileable"; public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; public static final String TAG_QUERIES = "queries"; public static final String TAG_RECEIVER = "receiver"; public static final String TAG_RESTRICT_UPDATE = "restrict-update"; public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_SUPPORTS_INPUT = "supports-input"; public static final String TAG_SUPPORT_SCREENS = "supports-screens"; public static final String TAG_USES_CONFIGURATION = "uses-configuration"; public static final String TAG_USES_FEATURE = "uses-feature"; public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; Loading @@ -196,8 +200,6 @@ public class ParsingPackageUtils { public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; public static final String TAG_USES_SDK = "uses-sdk"; public static final String TAG_USES_SPLIT = "uses-split"; public static final String TAG_PROFILEABLE = "profileable"; public static final String TAG_RECEIVER = "receiver"; public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes"; Loading Loading @@ -1040,6 +1042,8 @@ public class ParsingPackageUtils { return input.success(pkg); case TAG_RESTRICT_UPDATE: return parseRestrictUpdateHash(flags, input, pkg, res, parser); case TAG_INSTALL_CONSTRAINTS: return parseInstallConstraints(input, pkg, res, parser); case TAG_QUERIES: return parseQueries(input, pkg, res, parser); default: Loading Loading @@ -1729,6 +1733,12 @@ public class ParsingPackageUtils { return input.success(pkg); } private static ParseResult<ParsingPackage> parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); } private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { final int depth = parser.getDepth(); Loading
services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +53 −0 Original line number Diff line number Diff line Loading @@ -336,6 +336,59 @@ public class SystemConfigTest { assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } /** * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. */ @Test public void readPermissions_installConstraints_successful() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getInstallConstraintsAllowlist()) .containsExactly("com.android.apex1"); } /** * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. */ @Test public void readPermissions_installConstraints_noPackage() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed/>\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); } /** * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed} * without {@link SystemConfig#ALLOW_VENDOR_APEX}. */ @Test public void readPermissions_installConstraints_noAppConfigs() throws IOException { final String contents = "<config>\n" + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + "</config>"; final File folder = createTempSubfolder("folder"); createTempFile(folder, "install-constraints-allowlist.xml", contents); readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); } @Test public void readApexPrivAppPermissions_addAllPermissions() throws Exception { Loading