Merge changes from topic "required-feature-tag"

* changes:
  Teach PackageManager to handle <required-(not-)feature>
  Teach appt2 to handle <required-(not-)feature>
This commit is contained in:
TreeHugger Robot
2020-10-06 09:26:22 +00:00
committed by Android (Google) Code Review
4 changed files with 233 additions and 94 deletions

View File

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

View File

@@ -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

View File

@@ -1063,17 +1063,23 @@ class UsesPermission : public ManifestExtractor::Element {
public:
UsesPermission() = default;
std::string name;
std::string requiredFeature;
std::string requiredNotFeature;
std::vector<std::string> requiredFeatures;
std::vector<std::string> requiredNotFeatures;
int32_t required = true;
int32_t maxSdkVersion = -1;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
requiredFeature = GetAttributeStringDefault(
FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
requiredNotFeature = GetAttributeStringDefault(
FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
std::string feature =
GetAttributeStringDefault(FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
if (!feature.empty()) {
requiredFeatures.push_back(feature);
}
feature = GetAttributeStringDefault(FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
if (!feature.empty()) {
requiredNotFeatures.push_back(feature);
}
required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
maxSdkVersion = GetAttributeIntegerDefault(
FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
@@ -1090,13 +1096,13 @@ class UsesPermission : public ManifestExtractor::Element {
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
if (!requiredFeature.empty()) {
printer->Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
}
if (!requiredNotFeature.empty()) {
printer->Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
}
printer->Print("\n");
for (const std::string& requiredFeature : requiredFeatures) {
printer->Print(StringPrintf(" required-feature='%s'\n", requiredFeature.data()));
}
for (const std::string& requiredNotFeature : requiredNotFeatures) {
printer->Print(StringPrintf(" required-not-feature='%s'\n", requiredNotFeature.data()));
}
if (required == 0) {
printer->Print(StringPrintf("optional-permission: name='%s'", name.data()));
if (maxSdkVersion >= 0) {
@@ -1116,6 +1122,38 @@ class UsesPermission : public ManifestExtractor::Element {
}
};
/** Represents <required-feature> elements. **/
class RequiredFeature : public ManifestExtractor::Element {
public:
RequiredFeature() = default;
std::string name;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
auto parent_stack = extractor()->parent_stack();
if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
uses_permission->requiredFeatures.push_back(name);
}
}
};
/** Represents <required-not-feature> elements. **/
class RequiredNotFeature : public ManifestExtractor::Element {
public:
RequiredNotFeature() = default;
std::string name;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
auto parent_stack = extractor()->parent_stack();
if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
uses_permission->requiredNotFeatures.push_back(name);
}
}
};
/** Represents <uses-permission-sdk-23> elements. **/
class UsesPermissionSdk23 : public ManifestExtractor::Element {
public:
@@ -1845,7 +1883,8 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
for (xml::Element* child : element->GetChildElements()) {
if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
|| child->name == "permission") {
auto permission_element = ManifestExtractor::Element::Inflate(this, child);
// Inflate the element and its descendants
auto permission_element = Visit(child);
manifest->AddChild(permission_element);
}
}
@@ -2237,38 +2276,40 @@ T* ElementCast(ManifestExtractor::Element* element) {
}
const std::unordered_map<std::string, bool> kTagCheck = {
{"action", std::is_base_of<Action, T>::value},
{"activity", std::is_base_of<Activity, T>::value},
{"application", std::is_base_of<Application, T>::value},
{"category", std::is_base_of<Category, T>::value},
{"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
{"feature-group", std::is_base_of<FeatureGroup, T>::value},
{"input-type", std::is_base_of<InputType, T>::value},
{"intent-filter", std::is_base_of<IntentFilter, T>::value},
{"meta-data", std::is_base_of<MetaData, T>::value},
{"manifest", std::is_base_of<Manifest, T>::value},
{"original-package", std::is_base_of<OriginalPackage, T>::value},
{"overlay", std::is_base_of<Overlay, T>::value},
{"package-verifier", std::is_base_of<PackageVerifier, T>::value},
{"permission", std::is_base_of<Permission, T>::value},
{"provider", std::is_base_of<Provider, T>::value},
{"receiver", std::is_base_of<Receiver, T>::value},
{"screen", std::is_base_of<Screen, T>::value},
{"service", std::is_base_of<Service, T>::value},
{"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
{"supports-input", std::is_base_of<SupportsInput, T>::value},
{"supports-screens", std::is_base_of<SupportsScreen, T>::value},
{"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
{"uses-feature", std::is_base_of<UsesFeature, T>::value},
{"uses-permission", std::is_base_of<UsesPermission, T>::value},
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-library", std::is_base_of<UsesLibrary, T>::value},
{"uses-package", std::is_base_of<UsesPackage, T>::value},
{"static-library", std::is_base_of<StaticLibrary, T>::value},
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
{"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
{"action", std::is_base_of<Action, T>::value},
{"activity", std::is_base_of<Activity, T>::value},
{"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
{"application", std::is_base_of<Application, T>::value},
{"category", std::is_base_of<Category, T>::value},
{"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
{"feature-group", std::is_base_of<FeatureGroup, T>::value},
{"input-type", std::is_base_of<InputType, T>::value},
{"intent-filter", std::is_base_of<IntentFilter, T>::value},
{"meta-data", std::is_base_of<MetaData, T>::value},
{"manifest", std::is_base_of<Manifest, T>::value},
{"original-package", std::is_base_of<OriginalPackage, T>::value},
{"overlay", std::is_base_of<Overlay, T>::value},
{"package-verifier", std::is_base_of<PackageVerifier, T>::value},
{"permission", std::is_base_of<Permission, T>::value},
{"provider", std::is_base_of<Provider, T>::value},
{"receiver", std::is_base_of<Receiver, T>::value},
{"required-feature", std::is_base_of<RequiredFeature, T>::value},
{"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
{"screen", std::is_base_of<Screen, T>::value},
{"service", std::is_base_of<Service, T>::value},
{"static-library", std::is_base_of<StaticLibrary, T>::value},
{"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
{"supports-input", std::is_base_of<SupportsInput, T>::value},
{"supports-screens", std::is_base_of<SupportsScreen, T>::value},
{"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
{"uses-feature", std::is_base_of<UsesFeature, T>::value},
{"uses-library", std::is_base_of<UsesLibrary, T>::value},
{"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
{"uses-package", std::is_base_of<UsesPackage, T>::value},
{"uses-permission", std::is_base_of<UsesPermission, T>::value},
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2288,39 +2329,41 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
const std::unordered_map<std::string,
std::function<std::unique_ptr<ManifestExtractor::Element>()>>
kTagCheck = {
{"action", &CreateType<Action>},
{"activity", &CreateType<Activity>},
{"application", &CreateType<Application>},
{"category", &CreateType<Category>},
{"compatible-screens", &CreateType<CompatibleScreens>},
{"feature-group", &CreateType<FeatureGroup>},
{"input-type", &CreateType<InputType>},
{"intent-filter",&CreateType<IntentFilter>},
{"manifest", &CreateType<Manifest>},
{"meta-data", &CreateType<MetaData>},
{"original-package", &CreateType<OriginalPackage>},
{"overlay", &CreateType<Overlay>},
{"package-verifier", &CreateType<PackageVerifier>},
{"permission", &CreateType<Permission>},
{"provider", &CreateType<Provider>},
{"receiver", &CreateType<Receiver>},
{"screen", &CreateType<Screen>},
{"service", &CreateType<Service>},
{"supports-gl-texture", &CreateType<SupportsGlTexture>},
{"supports-input", &CreateType<SupportsInput>},
{"supports-screens", &CreateType<SupportsScreen>},
{"uses-configuration", &CreateType<UsesConfiguarion>},
{"uses-feature", &CreateType<UsesFeature>},
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-library", &CreateType<UsesLibrary>},
{"static-library", &CreateType<StaticLibrary>},
{"uses-static-library", &CreateType<UsesStaticLibrary>},
{"uses-package", &CreateType<UsesPackage>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
{"uses-native-library", &CreateType<UsesNativeLibrary>},
};
{"action", &CreateType<Action>},
{"activity", &CreateType<Activity>},
{"additional-certificate", &CreateType<AdditionalCertificate>},
{"application", &CreateType<Application>},
{"category", &CreateType<Category>},
{"compatible-screens", &CreateType<CompatibleScreens>},
{"feature-group", &CreateType<FeatureGroup>},
{"input-type", &CreateType<InputType>},
{"intent-filter", &CreateType<IntentFilter>},
{"manifest", &CreateType<Manifest>},
{"meta-data", &CreateType<MetaData>},
{"original-package", &CreateType<OriginalPackage>},
{"overlay", &CreateType<Overlay>},
{"package-verifier", &CreateType<PackageVerifier>},
{"permission", &CreateType<Permission>},
{"provider", &CreateType<Provider>},
{"receiver", &CreateType<Receiver>},
{"required-feature", &CreateType<RequiredFeature>},
{"required-not-feature", &CreateType<RequiredNotFeature>},
{"screen", &CreateType<Screen>},
{"service", &CreateType<Service>},
{"static-library", &CreateType<StaticLibrary>},
{"supports-gl-texture", &CreateType<SupportsGlTexture>},
{"supports-input", &CreateType<SupportsInput>},
{"supports-screens", &CreateType<SupportsScreen>},
{"uses-configuration", &CreateType<UsesConfiguarion>},
{"uses-feature", &CreateType<UsesFeature>},
{"uses-library", &CreateType<UsesLibrary>},
{"uses-native-library", &CreateType<UsesNativeLibrary>},
{"uses-package", &CreateType<UsesPackage>},
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
{"uses-static-library", &CreateType<UsesStaticLibrary>},
};
// Attempt to map the xml tag to a element inflater
std::unique_ptr<ManifestExtractor::Element> element;

View File

@@ -393,6 +393,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["protected-broadcast"];
manifest_action["adopt-permissions"];
manifest_action["uses-permission"];
manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty);
manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty);
manifest_action["uses-permission-sdk-23"];
manifest_action["permission"];
manifest_action["permission"]["meta-data"] = meta_data_action;