From 49237345d83e62fdb9eb8d50b13ad086636a04fa Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 27 Aug 2009 20:08:01 -0700 Subject: Add platform infrastructure for features. This introduces a new mechanism to define features associated with a platform, query the current device for the available features, and enforce that apps requiring features that aren't available can't be installed. Also now allows uses-library to specify that a library is optional, so the lack of such a library will not prevent the app from being installed (but if it does exist it will be correctly linked into the app). Change-Id: I5b369b46cfa0b3d37c9e08fd14ef1098a978e67b --- api/current.xml | 161 +++++++++++++++++++++ core/java/android/app/ApplicationContext.java | 10 ++ .../java/android/content/pm/ConfigurationInfo.java | 16 +- core/java/android/content/pm/FeatureInfo.aidl | 19 +++ core/java/android/content/pm/FeatureInfo.java | 101 +++++++++++++ core/java/android/content/pm/IPackageManager.aidl | 7 + core/java/android/content/pm/PackageInfo.java | 7 + core/java/android/content/pm/PackageManager.java | 24 ++- core/java/android/content/pm/PackageParser.java | 71 +++++++-- core/res/res/values/attrs_manifest.xml | 8 + data/etc/android.hardware.camera.autofocus.xml | 21 +++ .../com/android/server/PackageManagerService.java | 109 ++++++++++++-- .../android/test/mock/MockPackageManager.java | 6 + tools/aapt/Command.cpp | 7 +- 14 files changed, 527 insertions(+), 40 deletions(-) create mode 100755 core/java/android/content/pm/FeatureInfo.aidl create mode 100644 core/java/android/content/pm/FeatureInfo.java create mode 100644 data/etc/android.hardware.camera.autofocus.xml diff --git a/api/current.xml b/api/current.xml index 96e1d2d93382..dcf0d8485296 100644 --- a/api/current.xml +++ b/api/current.xml @@ -37589,6 +37589,134 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CREATOR = + new Creator() { + public FeatureInfo createFromParcel(Parcel source) { + return new FeatureInfo(source); + } + public FeatureInfo[] newArray(int size) { + return new FeatureInfo[size]; + } + }; + + private FeatureInfo(Parcel source) { + name = source.readString(); + reqGlEsVersion = source.readInt(); + flags = source.readInt(); + } + + /** + * This method extracts the major and minor version of reqGLEsVersion attribute + * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned + * as 1.2 + * @return String representation of the reqGlEsVersion attribute + */ + public String getGlEsVersion() { + int major = ((reqGlEsVersion & 0xffff0000) >> 16); + int minor = reqGlEsVersion & 0x0000ffff; + return String.valueOf(major)+"."+String.valueOf(minor); + } +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 4fc4fb946336..c322951f1813 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDataObserver; @@ -276,6 +277,12 @@ interface IPackageManager { */ String[] getSystemSharedLibraryNames(); + /** + * Get a list of features that are available on the + * system. + */ + FeatureInfo[] getSystemAvailableFeatures(); + void enterSafeMode(); boolean isSafeMode(); void systemReady(); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index d9326f211b5b..a8ce8890eba1 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -127,6 +127,11 @@ public class PackageInfo implements Parcelable { */ public ConfigurationInfo[] configPreferences; + /** + * The features that this application has said it requires. + */ + public FeatureInfo[] reqFeatures; + public PackageInfo() { } @@ -162,6 +167,7 @@ public class PackageInfo implements Parcelable { dest.writeStringArray(requestedPermissions); dest.writeTypedArray(signatures, parcelableFlags); dest.writeTypedArray(configPreferences, parcelableFlags); + dest.writeTypedArray(reqFeatures, parcelableFlags); } public static final Parcelable.Creator CREATOR @@ -195,5 +201,6 @@ public class PackageInfo implements Parcelable { requestedPermissions = source.createStringArray(); signatures = source.createTypedArray(Signature.CREATOR); configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR); + reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fca005cb039d..825eb85031d2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -159,8 +159,10 @@ public abstract class PackageManager { /** * {@link PackageInfo} flag: return information about - * hardware preferences - * {@link PackageInfo#configPreferences} + * hardware preferences in + * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and + * requested features in {@link PackageInfo#reqFeatures + * PackageInfo.reqFeatures}. */ public static final int GET_CONFIGURATIONS = 0x00004000; @@ -399,6 +401,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16; + /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the new package uses a feature that is not available. + * @hide + */ + public static final int INSTALL_FAILED_MISSING_FEATURE = -17; + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} @@ -979,6 +989,16 @@ public abstract class PackageManager { */ public abstract String[] getSystemSharedLibraryNames(); + /** + * Get a list of features that are available on the + * system. + * + * @return An array of FeatureInfo classes describing the features + * that are available on the system, or null if there are none(!!). + * + */ + public abstract FeatureInfo[] getSystemAvailableFeatures(); + /** * Determine the best action to perform for a given Intent. This is how * {@link Intent#resolveActivity} finds an activity if a class has not diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 96c948645151..4399df405ab3 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -184,9 +184,12 @@ public class PackageParser { int N = p.configPreferences.size(); if (N > 0) { pi.configPreferences = new ConfigurationInfo[N]; - for (int i=0; i 0) { + pi.reqFeatures = new FeatureInfo[N]; + p.reqFeatures.toArray(pi.reqFeatures); } } if ((flags&PackageManager.GET_ACTIVITIES) != 0) { @@ -760,14 +763,32 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-feature")) { - ConfigurationInfo cPref = new ConfigurationInfo(); + FeatureInfo fi = new FeatureInfo(); sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesFeature); - cPref.reqGlEsVersion = sa.getInt( - com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion, - ConfigurationInfo.GL_ES_VERSION_UNDEFINED); + 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(); - pkg.configPreferences.add(cPref); + if (pkg.reqFeatures == null) { + pkg.reqFeatures = new ArrayList(); + } + pkg.reqFeatures.add(fi); + + if (fi.name == null) { + ConfigurationInfo cPref = new ConfigurationInfo(); + cPref.reqGlEsVersion = fi.reqGlEsVersion; + pkg.configPreferences.add(cPref); + } XmlUtils.skipCurrentTag(parser); @@ -946,11 +967,6 @@ public class PackageParser { } } - if (pkg.usesLibraries.size() > 0) { - pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; - pkg.usesLibraries.toArray(pkg.usesLibraryFiles); - } - if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { @@ -1436,11 +1452,28 @@ public class PackageParser { String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); + boolean req = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestUsesLibrary_required, + true); sa.recycle(); - if (lname != null && !owner.usesLibraries.contains(lname)) { - owner.usesLibraries.add(lname.intern()); + if (lname != null) { + if (req) { + if (owner.usesLibraries == null) { + owner.usesLibraries = new ArrayList(); + } + if (!owner.usesLibraries.contains(lname)) { + owner.usesLibraries.add(lname.intern()); + } + } else { + if (owner.usesOptionalLibraries == null) { + owner.usesOptionalLibraries = new ArrayList(); + } + if (!owner.usesOptionalLibraries.contains(lname)) { + owner.usesOptionalLibraries.add(lname.intern()); + } + } } XmlUtils.skipCurrentTag(parser); @@ -2418,7 +2451,8 @@ public class PackageParser { public ArrayList protectedBroadcasts; - public final ArrayList usesLibraries = new ArrayList(); + public ArrayList usesLibraries = null; + public ArrayList usesOptionalLibraries = null; public String[] usesLibraryFiles = null; // We store the application meta-data independently to avoid multiple unwanted references @@ -2466,6 +2500,11 @@ public class PackageParser { public final ArrayList configPreferences = new ArrayList(); + /* + * Applications requested features + */ + public ArrayList reqFeatures = null; + public Package(String _name) { packageName = _name; applicationInfo.packageName = _name; diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d1079d03883b..ce421dbad320 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -832,6 +832,14 @@ + + + + + + + + diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 2f4d7162afcd..8ec6ec38e851 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -35,6 +35,7 @@ import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; +import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageInstallObserver; @@ -88,6 +89,7 @@ import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; @@ -221,6 +223,14 @@ class PackageManagerService extends IPackageManager.Stub { // etc/permissions.xml file. final HashMap mSharedLibraries = new HashMap(); + // Temporary for building the final shared libraries for an .apk. + String[] mTmpSharedLibraries = null; + + // These are the features this devices supports that were read from the + // etc/permissions.xml file. + final HashMap mAvailableFeatures = + new HashMap(); + // All available activities, for your resolving pleasure. final ActivityIntentResolver mActivities = new ActivityIntentResolver(); @@ -671,7 +681,21 @@ class PackageManagerService extends IPackageManager.Stub { + parser.getPositionDescription()); } else { Log.i(TAG, "Got library " + lname + " in " + lfile); - this.mSharedLibraries.put(lname, lfile); + mSharedLibraries.put(lname, lfile); + } + XmlUtils.skipCurrentTag(parser); + continue; + + } else if ("feature".equals(name)) { + String fname = parser.getAttributeValue(null, "name"); + if (fname == null) { + Log.w(TAG, " without name at " + + parser.getPositionDescription()); + } else { + Log.i(TAG, "Got feature " + fname); + FeatureInfo fi = new FeatureInfo(); + fi.name = fname; + mAvailableFeatures.put(fname, fi); } XmlUtils.skipCurrentTag(parser); continue; @@ -1001,12 +1025,30 @@ class PackageManagerService extends IPackageManager.Stub { Set libSet; synchronized (mPackages) { libSet = mSharedLibraries.keySet(); + int size = libSet.size(); + if (size > 0) { + String[] libs = new String[size]; + libSet.toArray(libs); + return libs; + } } - int size = libSet.size(); - if (size > 0) { - String[] libs = new String[size]; - libSet.toArray(libs); - return libs; + return null; + } + + public FeatureInfo[] getSystemAvailableFeatures() { + Collection featSet; + synchronized (mPackages) { + featSet = mAvailableFeatures.values(); + int size = featSet.size(); + if (size > 0) { + FeatureInfo[] features = new FeatureInfo[size+1]; + featSet.toArray(features); + FeatureInfo fi = new FeatureInfo(); + fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", + FeatureInfo.GL_ES_VERSION_UNDEFINED); + features[size] = fi; + return features; + } } return null; } @@ -2065,17 +2107,62 @@ class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { // Check all shared libraries and map to their actual file path. - if (pkg.usesLibraryFiles != null) { - for (int i=0; i 0) { + pkg.usesLibraryFiles = new String[num]; + System.arraycopy(mTmpSharedLibraries, 0, + pkg.usesLibraryFiles, 0, num); + } + + if (pkg.reqFeatures != null) { + N = pkg.reqFeatures.size(); + for (int i=0; i