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
diff --git a/api/current.xml b/api/current.xml
index 96e1d2d..dcf0d84 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -37589,6 +37589,134 @@
 >
 </field>
 </class>
+<class name="FeatureInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="FeatureInfo"
+ type="android.content.pm.FeatureInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="FeatureInfo"
+ type="android.content.pm.FeatureInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="android.content.pm.FeatureInfo">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGlEsVersion"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_REQUIRED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GL_ES_VERSION_UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reqGlEsVersion"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="InstrumentationInfo"
  extends="android.content.pm.PackageItemInfo"
  abstract="false"
@@ -37850,6 +37978,17 @@
  visibility="public"
 >
 </field>
+<field name="reqFeatures"
+ type="android.content.pm.FeatureInfo[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="requestedPermissions"
  type="java.lang.String[]"
  transient="false"
@@ -38719,6 +38858,17 @@
 <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
 </exception>
 </method>
+<method name="getSystemAvailableFeatures"
+ return="android.content.pm.FeatureInfo[]"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getSystemSharedLibraryNames"
  return="java.lang.String[]"
  abstract="true"
@@ -122943,6 +123093,17 @@
 <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
 </exception>
 </method>
+<method name="getSystemAvailableFeatures"
+ return="android.content.pm.FeatureInfo[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getSystemSharedLibraryNames"
  return="java.lang.String[]"
  abstract="false"
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 7c0d1d3..487cfda 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -40,6 +40,7 @@
 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;
@@ -1623,6 +1624,15 @@
         }
 
         @Override
+        public FeatureInfo[] getSystemAvailableFeatures() {
+            try {
+                return mPM.getSystemAvailableFeatures();
+            } catch (RemoteException e) {
+                throw new RuntimeException("Package manager has died", e);
+            }
+        }
+        
+        @Override
         public int checkPermission(String permName, String pkgName) {
             try {
                 return mPM.checkPermission(permName, pkgName);
diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java
index fb7a47f..8edd436 100755
--- a/core/java/android/content/pm/ConfigurationInfo.java
+++ b/core/java/android/content/pm/ConfigurationInfo.java
@@ -22,9 +22,9 @@
 /**
  * Information you can retrieve about hardware configuration preferences
  * declared by an application. This corresponds to information collected from the
- * AndroidManifest.xml's &lt;uses-configuration&gt; and the   &lt;uses-feature&gt;tags.
+ * AndroidManifest.xml's &lt;uses-configuration&gt; and &lt;uses-feature&gt; tags.
  */
-public class ConfigurationInfo implements Parcelable {    
+public class ConfigurationInfo implements Parcelable {
     /**
      * The kind of touch screen attached to the device.
      * One of: {@link android.content.res.Configuration#TOUCHSCREEN_NOTOUCH},
@@ -92,13 +92,13 @@
     }
 
     public String toString() {
-        return "ApplicationHardwarePreferences{"
+        return "ConfigurationInfo{"
             + Integer.toHexString(System.identityHashCode(this))
-            + ", touchscreen = " + reqTouchScreen + "}"
-            + ", inputMethod = " + reqKeyboardType + "}"
-            + ", navigation = " + reqNavigation + "}"
-            + ", reqInputFeatures = " + reqInputFeatures + "}"
-            + ", reqGlEsVersion = " + reqGlEsVersion + "}";
+            + " touchscreen = " + reqTouchScreen
+            + " inputMethod = " + reqKeyboardType
+            + " navigation = " + reqNavigation
+            + " reqInputFeatures = " + reqInputFeatures
+            + " reqGlEsVersion = " + reqGlEsVersion + "}";
     }
 
     public int describeContents() {
diff --git a/core/java/android/content/pm/FeatureInfo.aidl b/core/java/android/content/pm/FeatureInfo.aidl
new file mode 100755
index 0000000..d84a84c
--- /dev/null
+++ b/core/java/android/content/pm/FeatureInfo.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2009, 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;
+
+parcelable FeatureInfo;
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
new file mode 100644
index 0000000..57d61fd
--- /dev/null
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -0,0 +1,101 @@
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Parcelable.Creator;
+
+/**
+ * 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.
+ */
+public class FeatureInfo implements Parcelable {
+    /**
+     * The name of this feature, for example "android.hardware.camera".  If
+     * this is null, then this is an OpenGL ES version feature as described
+     * in {@link #reqGlEsVersion}.
+     */
+    public String name;
+    
+    /**
+     * Default value for {@link #reqGlEsVersion};
+     */
+    public static final int GL_ES_VERSION_UNDEFINED = 0;
+    
+    /**
+     * The GLES version used by an application. The upper order 16 bits represent the
+     * major version and the lower order 16 bits the minor version.  Only valid
+     * if {@link #name} is null.
+     */
+    public int reqGlEsVersion;
+
+    /**
+     * Set on {@link #flags} if this feature has been required by the application.
+     */
+    public static final int FLAG_REQUIRED = 0x0001;
+    
+    /**
+     * Additional flags.  May be zero or more of {@link #FLAG_REQUIRED}.
+     */
+    public int flags;
+    
+    public FeatureInfo() {
+    }
+
+    public FeatureInfo(FeatureInfo orig) {
+        name = orig.name;
+        reqGlEsVersion = orig.reqGlEsVersion;
+        flags = orig.flags;
+    }
+
+    public String toString() {
+        if (name != null) {
+            return "FeatureInfo{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " " + name + " fl=0x" + Integer.toHexString(flags) + "}";
+        } else {
+            return "FeatureInfo{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " glEsVers=" + getGlEsVersion()
+                    + " fl=0x" + Integer.toHexString(flags) + "}";
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString(name);
+        dest.writeInt(reqGlEsVersion);
+        dest.writeInt(flags);
+    }
+
+    public static final Creator<FeatureInfo> CREATOR =
+        new Creator<FeatureInfo>() {
+        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 4fc4fb9..c322951 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -22,6 +22,7 @@
 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 @@
      */
     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 d9326f2..a8ce889 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -127,6 +127,11 @@
      */
     public ConfigurationInfo[] configPreferences;
 
+    /**
+     * The features that this application has said it requires.
+     */
+    public FeatureInfo[] reqFeatures;
+    
     public PackageInfo() {
     }
 
@@ -162,6 +167,7 @@
         dest.writeStringArray(requestedPermissions);
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
+        dest.writeTypedArray(reqFeatures, parcelableFlags);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -195,5 +201,6 @@
         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 fca005c..825eb85 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -159,8 +159,10 @@
     
     /**
      * {@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;
 
@@ -400,6 +402,14 @@
     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)}
      * if the parser was given a path that is not a file, or does not end with the expected
@@ -980,6 +990,16 @@
     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
      * been explicitly specified.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 96c9486..4399df4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -184,9 +184,12 @@
             int N = p.configPreferences.size();
             if (N > 0) {
                 pi.configPreferences = new ConfigurationInfo[N];
-                for (int i=0; i<N; i++) {
-                    pi.configPreferences[i] = p.configPreferences.get(i);
-                }
+                p.configPreferences.toArray(pi.configPreferences);
+            }
+            N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
+            if (N > 0) {
+                pi.reqFeatures = new FeatureInfo[N];
+                p.reqFeatures.toArray(pi.reqFeatures);
             }
         }
         if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
@@ -760,14 +763,32 @@
                 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<FeatureInfo>();
+                }
+                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 @@
             }
         }
         
-        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 @@
 
                 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<String>();
+                        }
+                        if (!owner.usesLibraries.contains(lname)) {
+                            owner.usesLibraries.add(lname.intern());
+                        }
+                    } else {
+                        if (owner.usesOptionalLibraries == null) {
+                            owner.usesOptionalLibraries = new ArrayList<String>();
+                        }
+                        if (!owner.usesOptionalLibraries.contains(lname)) {
+                            owner.usesOptionalLibraries.add(lname.intern());
+                        }
+                    }
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -2418,7 +2451,8 @@
 
         public ArrayList<String> protectedBroadcasts;
         
-        public final ArrayList<String> usesLibraries = new ArrayList<String>();
+        public ArrayList<String> usesLibraries = null;
+        public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
 
         // We store the application meta-data independently to avoid multiple unwanted references
@@ -2466,6 +2500,11 @@
         public final ArrayList<ConfigurationInfo> configPreferences =
                 new ArrayList<ConfigurationInfo>();
 
+        /*
+         *  Applications requested features
+         */
+        public ArrayList<FeatureInfo> 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 d1079d0..ce421db 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -832,6 +832,14 @@
     <declare-styleable name="AndroidManifestUsesLibrary" parent="AndroidManifestApplication">
         <!-- Required name of the library you use. -->
         <attr name="name" />
+        <!--  Specify whether this library is required for the application.
+              The default is true, meaning the application requires the
+              library, and does not want to be installed on devices that
+              don't support it.  If you set this to false, then this will
+              allow the application to be installed even if the library
+              doesn't exist, and you will need to check for its presence
+              dynamically at runtime. -->
+        <attr name="required" />
     </declare-styleable>
     
     <!-- The <code>supports-screens</code> specifies the screen dimensions an
diff --git a/data/etc/android.hardware.camera.autofocus.xml b/data/etc/android.hardware.camera.autofocus.xml
new file mode 100644
index 0000000..d6e2b90
--- /dev/null
+++ b/data/etc/android.hardware.camera.autofocus.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- This is the standard set of features for an auto-focus camera. -->
+<permissions>
+    <feature name="android.hardware.camera" />
+    <feature name="android.hardware.camera.autofocus" />
+</permissions>
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 2f4d716..8ec6ec3 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -35,6 +35,7 @@
 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.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 @@
     // etc/permissions.xml file.
     final HashMap<String, String> mSharedLibraries = new HashMap<String, String>();
     
+    // 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<String, FeatureInfo> mAvailableFeatures =
+            new HashMap<String, FeatureInfo>();
+    
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
             new ActivityIntentResolver();
@@ -671,7 +681,21 @@
                                 + 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, "<feature> 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 @@
         Set<String> 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<FeatureInfo> 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 @@
         
         synchronized (mPackages) {
             // Check all shared libraries and map to their actual file path.
-            if (pkg.usesLibraryFiles != null) {
-                for (int i=0; i<pkg.usesLibraryFiles.length; i++) {
-                    String file = mSharedLibraries.get(pkg.usesLibraryFiles[i]);
+            if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
+                if (mTmpSharedLibraries == null ||
+                        mTmpSharedLibraries.length < mSharedLibraries.size()) {
+                    mTmpSharedLibraries = new String[mSharedLibraries.size()];
+                }
+                int num = 0;
+                int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
+                for (int i=0; i<N; i++) {
+                    String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
                     if (file == null) {
                         Log.e(TAG, "Package " + pkg.packageName
                                 + " requires unavailable shared library "
-                                + pkg.usesLibraryFiles[i] + "; ignoring!");
+                                + pkg.usesLibraries.get(i) + "; failing!");
                         mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
                         return null;
                     }
-                    pkg.usesLibraryFiles[i] = file;
+                    mTmpSharedLibraries[num] = file;
+                    num++;
+                }
+                N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
+                for (int i=0; i<N; i++) {
+                    String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+                    if (file == null) {
+                        Log.w(TAG, "Package " + pkg.packageName
+                                + " desires unavailable shared library "
+                                + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
+                    } else {
+                        mTmpSharedLibraries[num] = file;
+                        num++;
+                    }
+                }
+                if (num > 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<N; i++) {
+                        FeatureInfo fi = pkg.reqFeatures.get(i);
+                        if ((fi.flags&FeatureInfo.FLAG_REQUIRED) == 0) {
+                            // Don't care.
+                            continue;
+                        }
+                        
+                        if (fi.name != null) {
+                            if (mAvailableFeatures.get(fi.name) == null) {
+                                Log.e(TAG, "Package " + pkg.packageName
+                                        + " requires unavailable feature "
+                                        + fi.name + "; failing!");
+                                mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE;
+                                return null;
+                            }
+                        }
+                    }
                 }
             }
             
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 4a50c5d..beb9044 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -22,6 +22,7 @@
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
@@ -424,6 +425,11 @@
     }
     
     @Override
+    public FeatureInfo[] getSystemAvailableFeatures() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
     public boolean isSafeMode() {
         throw new UnsupportedOperationException();
     }
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index f4afd7f..790b474 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -730,8 +730,6 @@
                     } else if (tag == "uses-permission") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
                         if (error == "") {
-                            int opt = getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, NULL, 1);
                             if (name == "android.permission.CAMERA") {
                                 hasCameraPermission = true;
                             }
@@ -772,7 +770,10 @@
                             fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
                             goto bail;
                         }
-                        printf("uses-library:'%s'\n", libraryName.string());
+                        int req = getIntegerAttribute(tree,
+                                REQUIRED_ATTR, NULL, 1);
+                        printf("uses-library%s:'%s'\n",
+                                req ? "" : "-not-required", libraryName.string());
                     } else if (tag == "receiver") {
                         withinReceiver = true;
                         receiverName = getAttribute(tree, NAME_ATTR, &error);