diff options
| -rw-r--r-- | core/java/android/content/pm/parsing/ParsingPackageUtils.java | 108 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 16 |
2 files changed, 109 insertions, 15 deletions
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 9197020e1fce..b936c6323a80 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1080,14 +1080,57 @@ public class ParsingPackageUtils { } } - final String requiredFeature = sa.getNonConfigurationString( - R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); + final ArraySet<String> requiredFeatures = new ArraySet<>(); + String feature = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, + 0); + if (feature != null) { + requiredFeatures.add(feature); + } - final String requiredNotfeature = sa.getNonConfigurationString( - R.styleable.AndroidManifestUsesPermission_requiredNotFeature, + final ArraySet<String> requiredNotFeatures = new ArraySet<>(); + feature = sa.getNonConfigurationString( + com.android.internal.R.styleable + .AndroidManifestUsesPermission_requiredNotFeature, 0); + if (feature != null) { + requiredNotFeatures.add(feature); + } + + final 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; + } + + final ParseResult<?> result; + switch (parser.getName()) { + case "required-feature": + result = parseRequiredFeature(input, res, parser); + if (result.isSuccess()) { + requiredFeatures.add((String) result.getResult()); + } + break; + + case "required-not-feature": + result = parseRequiredNotFeature(input, res, parser); + if (result.isSuccess()) { + requiredNotFeatures.add((String) result.getResult()); + } + break; - XmlUtils.skipCurrentTag(parser); + default: + result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input); + break; + } + + if (result.isError()) { + return input.error(result); + } + } // Can only succeed from here on out ParseResult<ParsingPackage> success = input.success(pkg); @@ -1100,17 +1143,22 @@ public class ParsingPackageUtils { return success; } - // Only allow requesting this permission if the platform supports the given feature. - if (requiredFeature != null && mCallback != null && !mCallback.hasFeature( - requiredFeature)) { - return success; - } + if (mCallback != null) { + // Only allow requesting this permission if the platform supports all of the + // "required-feature"s. + for (int i = requiredFeatures.size() - 1; i >= 0; i--) { + if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) { + return success; + } + } - // Only allow requesting this permission if the platform doesn't support the given - // feature. - if (requiredNotfeature != null && mCallback != null - && mCallback.hasFeature(requiredNotfeature)) { - return success; + // Only allow requesting this permission if the platform does not supports any of + // the "required-not-feature"s. + for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) { + if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) { + return success; + } + } } if (!pkg.getRequestedPermissions().contains(name)) { @@ -1127,6 +1175,36 @@ public class ParsingPackageUtils { } } + private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from <required-feature> tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + + private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredNotFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredNotFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from <required-not-feature> tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) { ConfigurationInfo cPref = new ConfigurationInfo(); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 96ebc127e9ba..25c64a9f8781 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2016,6 +2016,22 @@ <attr name="requiredNotFeature" format="string" /> </declare-styleable> + <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside + <code>uses-permission<code/> can be used to request the permission based on the fact + whether the system supports or does not support certain features. + If multiple <code>required-feature</code> and/or <code>required-not-feature</code> elements + are present, the permission will be “requested” only if the system supports all of the + listed "required-features" and does not support any of the "required-not-features". + --> + <declare-styleable name="AndroidManifestRequiredFeature"> + <!-- The name of the feature. --> + <attr name="name" /> + </declare-styleable> + <declare-styleable name="AndroidManifestRequiredNotFeature"> + <!-- The name of the feature. --> + <attr name="name" /> + </declare-styleable> + <!-- The <code>uses-configuration</code> tag specifies a specific hardware configuration value used by the application. For example an application might specify that it requires |