summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt10
-rw-r--r--core/java/android/content/pm/FeatureGroupInfo.java65
-rw-r--r--core/java/android/content/pm/FeatureInfo.java2
-rw-r--r--core/java/android/content/pm/PackageInfo.java14
-rw-r--r--core/java/android/content/pm/PackageManager.java6
-rw-r--r--core/java/android/content/pm/PackageParser.java79
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--tests/UsesFeature2Test/AndroidManifest.xml9
-rw-r--r--tools/aapt/Command.cpp8
-rw-r--r--tools/aapt/Resource.cpp35
10 files changed, 192 insertions, 40 deletions
diff --git a/api/current.txt b/api/current.txt
index 5a874fe..5778c7e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8526,6 +8526,15 @@ package android.content.pm {
field public int reqTouchScreen;
}
+ public final class FeatureGroupInfo implements android.os.Parcelable {
+ ctor public FeatureGroupInfo();
+ ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public android.content.pm.FeatureInfo[] features;
+ }
+
public class FeatureInfo implements android.os.Parcelable {
ctor public FeatureInfo();
ctor public FeatureInfo(android.content.pm.FeatureInfo);
@@ -8617,6 +8626,7 @@ package android.content.pm {
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public android.content.pm.ConfigurationInfo[] configPreferences;
+ field public android.content.pm.FeatureGroupInfo[] featureGroups;
field public long firstInstallTime;
field public int[] gids;
field public int installLocation;
diff --git a/core/java/android/content/pm/FeatureGroupInfo.java b/core/java/android/content/pm/FeatureGroupInfo.java
new file mode 100644
index 0000000..79a6eea
--- /dev/null
+++ b/core/java/android/content/pm/FeatureGroupInfo.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2014 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 android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A set of features that can be requested by an application. This corresponds
+ * to information collected from the
+ * AndroidManifest.xml's {@code <feature-group>} tag.
+ */
+public final class FeatureGroupInfo implements Parcelable {
+
+ /**
+ * The list of features that are required by this group.
+ *
+ * @see FeatureInfo#FLAG_REQUIRED
+ */
+ public FeatureInfo[] features;
+
+ public FeatureGroupInfo() {
+ }
+
+ public FeatureGroupInfo(FeatureGroupInfo other) {
+ features = other.features;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedArray(features, flags);
+ }
+
+ public static final Creator<FeatureGroupInfo> CREATOR = new Creator<FeatureGroupInfo>() {
+ @Override
+ public FeatureGroupInfo createFromParcel(Parcel source) {
+ FeatureGroupInfo group = new FeatureGroupInfo();
+ group.features = source.createTypedArray(FeatureInfo.CREATOR);
+ return group;
+ }
+
+ @Override
+ public FeatureGroupInfo[] newArray(int size) {
+ return new FeatureGroupInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index d919fc3..79fa327 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -22,7 +22,7 @@ import android.os.Parcelable;
/**
* A single feature that can be requested by an application. This corresponds
* to information collected from the
- * AndroidManifest.xml's &lt;uses-feature&gt; tag.
+ * AndroidManifest.xml's {@code <uses-feature>} tag.
*/
public class FeatureInfo implements Parcelable {
/**
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 49ffef2..a0e3c4a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -180,7 +180,7 @@ public class PackageInfo implements Parcelable {
* {@link android.R.styleable#AndroidManifestUsesConfiguration
* &lt;uses-configuration&gt;} tags included under &lt;manifest&gt;,
* or null if there were none. This is only filled in if the flag
- * {@link PackageManager#GET_CONFIGURATIONS} was set.
+ * {@link PackageManager#GET_CONFIGURATIONS} was set.
*/
public ConfigurationInfo[] configPreferences;
@@ -192,6 +192,16 @@ public class PackageInfo implements Parcelable {
public FeatureInfo[] reqFeatures;
/**
+ * Groups of features that this application has requested.
+ * Each group contains a set of features that are required.
+ * A device must match the features listed in {@link #reqFeatures} and one
+ * or more FeatureGroups in order to have satisfied the feature requirement.
+ *
+ * @see FeatureInfo#FLAG_REQUIRED
+ */
+ public FeatureGroupInfo[] featureGroups;
+
+ /**
* Constant corresponding to <code>auto</code> in
* the {@link android.R.attr#installLocation} attribute.
* @hide
@@ -300,6 +310,7 @@ public class PackageInfo implements Parcelable {
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
+ dest.writeTypedArray(featureGroups, parcelableFlags);
dest.writeInt(installLocation);
dest.writeInt(requiredForAllUsers ? 1 : 0);
dest.writeInt(requiredForProfile);
@@ -344,6 +355,7 @@ public class PackageInfo implements Parcelable {
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+ featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
installLocation = source.readInt();
requiredForAllUsers = source.readInt() != 0;
requiredForProfile = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5a54767..56b7164 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -177,9 +177,9 @@ public abstract class PackageManager {
/**
* {@link PackageInfo} flag: return information about
* hardware preferences in
- * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
- * requested features in {@link PackageInfo#reqFeatures
- * PackageInfo.reqFeatures}.
+ * {@link PackageInfo#configPreferences PackageInfo.configPreferences},
+ * and requested features in {@link PackageInfo#reqFeatures} and
+ * {@link PackageInfo#featureGroups}.
*/
public static final int GET_CONFIGURATIONS = 0x00004000;
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44bf35d..cddefb5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -434,6 +434,11 @@ public class PackageParser {
pi.reqFeatures = new FeatureInfo[N];
p.reqFeatures.toArray(pi.reqFeatures);
}
+ N = p.featureGroups != null ? p.featureGroups.size() : 0;
+ if (N > 0) {
+ pi.featureGroups = new FeatureGroupInfo[N];
+ p.featureGroups.toArray(pi.featureGroups);
+ }
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
int N = p.activities.size();
@@ -1502,24 +1507,7 @@ public class PackageParser {
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-feature")) {
- FeatureInfo fi = new FeatureInfo();
- sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestUsesFeature);
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- fi.name = sa.getNonResourceString(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
- if (fi.name == null) {
- fi.reqGlEsVersion = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- }
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
- true)) {
- fi.flags |= FeatureInfo.FLAG_REQUIRED;
- }
- sa.recycle();
+ FeatureInfo fi = parseUsesFeature(res, attrs);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
if (fi.name == null) {
@@ -1531,9 +1519,35 @@ public class PackageParser {
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("feature-group")) {
- // Skip this for now until we know what to do with it.
+ FeatureGroupInfo group = new FeatureGroupInfo();
+ ArrayList<FeatureInfo> features = null;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
- XmlUtils.skipCurrentTag(parser);
+ final String innerTagName = parser.getName();
+ if (innerTagName.equals("uses-feature")) {
+ FeatureInfo featureInfo = parseUsesFeature(res, attrs);
+ // FeatureGroups are stricter and mandate that
+ // any <uses-feature> declared are mandatory.
+ featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+ features = ArrayUtils.add(features, featureInfo);
+ } else {
+ Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
+ " at " + mArchiveSourcePath + " " +
+ parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ if (features != null) {
+ group.features = new FeatureInfo[features.size()];
+ group.features = features.toArray(group.features);
+ }
+ pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
} else if (tagName.equals("uses-sdk")) {
if (SDK_VERSION > 0) {
@@ -1851,6 +1865,28 @@ public class PackageParser {
return pkg;
}
+ private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
+ FeatureInfo fi = new FeatureInfo();
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestUsesFeature);
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ fi.name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+ if (fi.name == null) {
+ fi.reqGlEsVersion = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ }
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) {
+ fi.flags |= FeatureInfo.FLAG_REQUIRED;
+ }
+ sa.recycle();
+ return fi;
+ }
+
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
@@ -4225,6 +4261,9 @@ public class PackageParser {
// Applications requested features
public ArrayList<FeatureInfo> reqFeatures = null;
+ // Applications requested feature groups
+ public ArrayList<FeatureGroupInfo> featureGroups = null;
+
public int installLocation;
/* An app that's required for all users and cannot be uninstalled for a user */
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c268d97..7d4c37e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1250,7 +1250,9 @@
application.
<p>This appears as a child tag of the root
- {@link #AndroidManifest manifest} tag. -->
+ {@link #AndroidManifest manifest} tag.
+
+ @deprecated Use <code>feature-group</code> instead.-->
<declare-styleable name="AndroidManifestUsesConfiguration" parent="AndroidManifest">
<!-- The type of touch screen used by an application. -->
<attr name="reqTouchScreen" />
diff --git a/tests/UsesFeature2Test/AndroidManifest.xml b/tests/UsesFeature2Test/AndroidManifest.xml
index 6b6c4da..8caf4a1 100644
--- a/tests/UsesFeature2Test/AndroidManifest.xml
+++ b/tests/UsesFeature2Test/AndroidManifest.xml
@@ -33,12 +33,5 @@
<uses-feature android:name="android.hardware.opengles.aep" />
</feature-group>
- <application android:label="@string/app_title">
- <activity android:name="ActivityMain">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
+ <application android:label="@string/app_title" android:hasCode="false" />
</manifest>
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index c74a373..fe0a601 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1673,12 +1673,8 @@ int doDump(Bundle* bundle)
String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
- top.features.add(name, required);
- if (required) {
- addParentFeatures(&top, name);
- }
-
+ top.features.add(name, true);
+ addParentFeatures(&top, name);
} else {
int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
if (error == "") {
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 010d59b..0a80805 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1470,6 +1470,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil
String16 action16("action");
String16 category16("category");
String16 data16("scheme");
+ String16 feature_group16("feature-group");
+ String16 uses_feature16("uses-feature");
const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
@@ -1680,10 +1682,43 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil
schemeIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
+ } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) {
+ int depth = 1;
+ while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+ && code > ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::START_TAG) {
+ depth++;
+ if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) {
+ ssize_t idx = block.indexOfAttribute(
+ RESOURCES_ANDROID_NAMESPACE, "required");
+ if (idx < 0) {
+ continue;
+ }
+
+ int32_t data = block.getAttributeData(idx);
+ if (data == 0) {
+ fprintf(stderr, "%s:%d: Tag <uses-feature> can not have "
+ "android:required=\"false\" when inside a "
+ "<feature-group> tag.\n",
+ manifestPath.string(), block.getLineNumber());
+ hasErrors = true;
+ }
+ }
+ } else if (code == ResXMLTree::END_TAG) {
+ depth--;
+ if (depth == 0) {
+ break;
+ }
+ }
+ }
}
}
}
+ if (hasErrors) {
+ return UNKNOWN_ERROR;
+ }
+
if (resFile != NULL) {
// These resources are now considered to be a part of the included
// resources, for others to reference.