Teach PackageManager to handle <required-(not-)feature>
Introducing 'required-feature' and 'requred-not-feature' tags that could be used "inside" 'uses-permission' elements. Elements of both types should have non-empty 'android:name' attribute to specify the name of the feature. If the 'uses-permission' element contains 'required-feature' and/or 'required-not-feature' elements, the permission will only be "requested" if the system supports all the 'required-feature's and does not support any of the 'required-not-feature's. The new elemets are "replacement" of the existing 'android:required(Not)Feature' attributes that can be optionally added to 'uses-permission' elements, but allow to specify only 1 feature each. However the 'android:required(Not)Feature' attributes remain supported for compatibility and will be treated similarly to the 'require-(not-)feature' elements. Bug: 168079571 Test: make && flash Test: atest CtsContentTestCases:PermissionFeatureTest Change-Id: Id3043530ebac6bd11805a611cebbf08b45e12f68
This commit is contained in:
@@ -1080,14 +1080,57 @@ public class ParsingPackageUtils {
|
||||
}
|
||||
}
|
||||
|
||||
final String requiredFeature = sa.getNonConfigurationString(
|
||||
R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
|
||||
|
||||
final String requiredNotfeature = sa.getNonConfigurationString(
|
||||
R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
|
||||
final ArraySet<String> requiredFeatures = new ArraySet<>();
|
||||
String feature = sa.getNonConfigurationString(
|
||||
com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
|
||||
0);
|
||||
if (feature != null) {
|
||||
requiredFeatures.add(feature);
|
||||
}
|
||||
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user