diff options
-rw-r--r-- | api/current.txt | 10 | ||||
-rw-r--r-- | core/java/android/content/pm/FeatureGroupInfo.java | 65 | ||||
-rw-r--r-- | core/java/android/content/pm/FeatureInfo.java | 2 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageInfo.java | 14 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageManager.java | 6 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 79 | ||||
-rw-r--r-- | core/res/res/values/attrs_manifest.xml | 4 | ||||
-rw-r--r-- | tests/UsesFeature2Test/AndroidManifest.xml | 9 | ||||
-rw-r--r-- | tools/aapt/Command.cpp | 8 | ||||
-rw-r--r-- | tools/aapt/Resource.cpp | 35 |
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 <uses-feature> 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 * <uses-configuration>} tags included under <manifest>, * 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. |