Add feature versions for devices and apps.

We're starting to see more instances of device features that will
increment separately from the SDK API level, such as camera HAL,
GPU capabilities, Bluetooth, and other hardware standards.

This change adds the ability for device features to specify a
version, which is defined to be backwards compatible.  That is, apps
requesting an older version of a feature must continue working on
devices with a newer version of that same feature.

When a version is undefined, we assume the default version "0".

Bug: 27162500
Change-Id: If890bf3f3dbb715e8feb80e7059a0d65618482ea
diff --git a/api/current.txt b/api/current.txt
index 3c0c64d..0337e11 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1376,6 +1376,7 @@
     field public static final int valueType = 16843488; // 0x10102e0
     field public static final int variablePadding = 16843157; // 0x1010195
     field public static final int vendor = 16843751; // 0x10103e7
+    field public static final int version = 16844058; // 0x101051a
     field public static final int versionCode = 16843291; // 0x101021b
     field public static final int versionName = 16843292; // 0x101021c
     field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -9400,6 +9401,7 @@
     field public int flags;
     field public java.lang.String name;
     field public int reqGlEsVersion;
+    field public int version;
   }
 
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -9678,6 +9680,7 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -37789,6 +37792,7 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 335557b..266900f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1475,6 +1475,7 @@
     field public static final int valueType = 16843488; // 0x10102e0
     field public static final int variablePadding = 16843157; // 0x1010195
     field public static final int vendor = 16843751; // 0x10103e7
+    field public static final int version = 16844058; // 0x101051a
     field public static final int versionCode = 16843291; // 0x101021b
     field public static final int versionName = 16843292; // 0x101021c
     field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -9745,6 +9746,7 @@
     field public int flags;
     field public java.lang.String name;
     field public int reqGlEsVersion;
+    field public int version;
   }
 
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -10027,6 +10029,7 @@
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -40538,6 +40541,7 @@
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6bd4887..15b1e78 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1376,6 +1376,7 @@
     field public static final int valueType = 16843488; // 0x10102e0
     field public static final int variablePadding = 16843157; // 0x1010195
     field public static final int vendor = 16843751; // 0x10103e7
+    field public static final int version = 16844058; // 0x101051a
     field public static final int versionCode = 16843291; // 0x101021b
     field public static final int versionName = 16843292; // 0x101021c
     field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -9407,6 +9408,7 @@
     field public int flags;
     field public java.lang.String name;
     field public int reqGlEsVersion;
+    field public int version;
   }
 
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
@@ -9686,6 +9688,7 @@
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
+    method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -37806,6 +37809,7 @@
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
+    method public boolean hasSystemFeature(java.lang.String, int);
     method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
index e0f09ee..8e9791f 100644
--- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -58,7 +58,7 @@
                 IPackageManager pm = IPackageManager.Stub.asInterface(
                         ServiceManager.getService("package"));
                 try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+                    if (pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0)) {
                         INfcAdapter nfc = INfcAdapter.Stub
                                 .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
                         try {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index df4b7d1..7e50518 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -450,8 +450,13 @@
 
     @Override
     public boolean hasSystemFeature(String name) {
+        return hasSystemFeature(name, 0);
+    }
+
+    @Override
+    public boolean hasSystemFeature(String name, int version) {
         try {
-            return mPM.hasSystemFeature(name);
+            return mPM.hasSystemFeature(name, version);
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index 79fa327..7671f72 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -20,9 +20,18 @@
 import android.os.Parcelable;
 
 /**
- * A single feature that can be requested by an application. This corresponds
- * to information collected from the
- * AndroidManifest.xml's {@code <uses-feature>} tag.
+ * Definition of a single optional hardware or software feature of an Android
+ * device.
+ * <p>
+ * This object is used to represent both features supported by a device and
+ * features requested by an app. Apps can request that certain features be
+ * available as a prerequisite to being installed through the
+ * {@code uses-feature} tag in their manifests.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N}, features can have a
+ * version, which must always be backwards compatible. That is, a device
+ * claiming to support version 3 of a specific feature must support apps
+ * requesting version 1 of that feature.
  */
 public class FeatureInfo implements Parcelable {
     /**
@@ -31,7 +40,17 @@
      * in {@link #reqGlEsVersion}.
      */
     public String name;
-    
+
+    /**
+     * If this object represents a feature supported by a device, this is the
+     * maximum version of this feature supported by the device. The device
+     * implicitly supports all older versions of this feature.
+     * <p>
+     * If this object represents a feature requested by an app, this is the
+     * minimum version of the feature required by the app.
+     */
+    public int version;
+
     /**
      * Default value for {@link #reqGlEsVersion};
      */
@@ -59,15 +78,17 @@
 
     public FeatureInfo(FeatureInfo orig) {
         name = orig.name;
+        version = orig.version;
         reqGlEsVersion = orig.reqGlEsVersion;
         flags = orig.flags;
     }
 
+    @Override
     public String toString() {
         if (name != null) {
             return "FeatureInfo{"
                     + Integer.toHexString(System.identityHashCode(this))
-                    + " " + name + " fl=0x" + Integer.toHexString(flags) + "}";
+                    + " " + name + " v=" + version + " fl=0x" + Integer.toHexString(flags) + "}";
         } else {
             return "FeatureInfo{"
                     + Integer.toHexString(System.identityHashCode(this))
@@ -76,21 +97,25 @@
         }
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(name);
+        dest.writeInt(version);
         dest.writeInt(reqGlEsVersion);
         dest.writeInt(flags);
     }
 
-    public static final Creator<FeatureInfo> CREATOR =
-        new Creator<FeatureInfo>() {
+    public static final Creator<FeatureInfo> CREATOR = new Creator<FeatureInfo>() {
+        @Override
         public FeatureInfo createFromParcel(Parcel source) {
             return new FeatureInfo(source);
         }
+        @Override
         public FeatureInfo[] newArray(int size) {
             return new FeatureInfo[size];
         }
@@ -98,6 +123,7 @@
 
     private FeatureInfo(Parcel source) {
         name = source.readString();
+        version = source.readInt();
         reqGlEsVersion = source.readInt();
         flags = source.readInt();
     }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3863857..c71a603 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -416,7 +416,7 @@
      */
     FeatureInfo[] getSystemAvailableFeatures();
 
-    boolean hasSystemFeature(String name);
+    boolean hasSystemFeature(String name, int version);
 
     void enterSafeMode();
     boolean isSafeMode();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bf0d4de..36b902c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3405,15 +3405,27 @@
     public abstract FeatureInfo[] getSystemAvailableFeatures();
 
     /**
-     * Check whether the given feature name is one of the available
-     * features as returned by {@link #getSystemAvailableFeatures()}.
+     * Check whether the given feature name is one of the available features as
+     * returned by {@link #getSystemAvailableFeatures()}. This tests for the
+     * presence of <em>any</em> version of the given feature name; use
+     * {@link #hasSystemFeature(String, int)} to check for a minimum version.
      *
-     * @return Returns true if the devices supports the feature, else
-     * false.
+     * @return Returns true if the devices supports the feature, else false.
      */
     public abstract boolean hasSystemFeature(String name);
 
     /**
+     * Check whether the given feature name and version is one of the available
+     * features as returned by {@link #getSystemAvailableFeatures()}. Since
+     * features are defined to always be backwards compatible, this returns true
+     * if the available feature version is greater than or equal to the
+     * requested version.
+     *
+     * @return Returns true if the devices supports the feature, else false.
+     */
+    public abstract boolean hasSystemFeature(String name, int version);
+
+    /**
      * 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 5ae8d4c..1ee19de 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2127,6 +2127,8 @@
         // that may change.
         fi.name = sa.getNonResourceString(
                 com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+        fi.version = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_version, 0);
         if (fi.name == null) {
             fi.reqGlEsVersion = sa.getInt(
                         com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index acd780d..6f911ce 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -414,7 +414,7 @@
             return false;
         }
         try {
-            return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+            return pm.hasSystemFeature(PackageManager.FEATURE_NFC, 0);
         } catch (RemoteException e) {
             Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
             return false;
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 23d05bd..b49288e 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -156,7 +156,7 @@
                 throw new UnsupportedOperationException();
             }
             try {
-                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
                     Log.e(TAG, "This device does not support card emulation");
                     throw new UnsupportedOperationException();
                 }
diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
index d61ac02..42ccf20 100644
--- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
+++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java
@@ -77,7 +77,7 @@
                 throw new UnsupportedOperationException();
             }
             try {
-                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+                if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, 0)) {
                     Log.e(TAG, "This device does not support NFC-F card emulation");
                     throw new UnsupportedOperationException();
                 }
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1496d09..f91bcd0 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1425,22 +1425,24 @@
         <attr name="reqFiveWayNav" />
     </declare-styleable>
 
-    <!-- The <code>uses-feature</code> tag specifies
-         a specific feature used by the application.
-         For example an application might specify that it requires
-         specific version of OpenGL. Multiple such attribute
-         values can be specified by the application.
+    <!-- The <code>uses-feature</code> tag specifies a specific device
+         hardware or software feature used by the application. For
+         example an application might specify that it requires
+         a camera. Multiple attribute values can be specified by the
+         application.
 
          <p>This appears as a child tag of the root
          {@link #AndroidManifest manifest} tag. -->
     <declare-styleable name="AndroidManifestUsesFeature" parent="AndroidManifest">
+        <!-- The name of the feature that is being used. -->
+        <attr name="name" />
+        <!-- The version of the feature that is being used. -->
+        <attr name="version" format="integer" />
         <!-- The GLES driver version number needed by an application.
              The higher 16 bits represent the major number and the lower 16 bits
              represent the minor number. For example for GL 1.2 referring to
              0x00000102, the actual value should be set as 0x00010002. -->
-        <attr name="glEsVersion" format="integer"/>
-        <!--  The name of the feature that is being used. -->
-        <attr name="name" />
+        <attr name="glEsVersion" format="integer" />
         <!--  Specify whether this feature is required for the application.
               The default is true, meaning the application requires the
               feature, and does not want to be installed on devices that
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 69d005c..894fd37 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2700,6 +2700,7 @@
     <public type="attr" name="bitmap" />
     <public type="attr" name="hotSpotX" />
     <public type="attr" name="hotSpotY" />
+    <public type="attr" name="version" />
 
     <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
     <public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 278dfe6..17e43c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -267,7 +267,7 @@
                     IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
             onlyCoreApps = packageManager.isOnlyCoreApps();
             freeformWindowManagement = packageManager.hasSystemFeature(
-                    PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+                    PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0);
         } catch (RemoteException e) {
             onlyCoreApps = false;
             freeformWindowManagement = false;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 5aba22d..19d7e2e 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -342,6 +342,7 @@
 
                 } else if ("feature".equals(name) && allowFeatures) {
                     String fname = parser.getAttributeValue(null, "name");
+                    int fversion = XmlUtils.readIntAttribute(null, "version", 0);
                     boolean allowed;
                     if (!lowRam) {
                         allowed = true;
@@ -353,7 +354,7 @@
                         Slog.w(TAG, "<feature> without name in " + permFile + " at "
                                 + parser.getPositionDescription());
                     } else if (allowed) {
-                        addFeature(fname);
+                        addFeature(fname, fversion);
                     }
                     XmlUtils.skipCurrentTag(parser);
                     continue;
@@ -445,8 +446,8 @@
         // Some devices can be field-converted to FBE, so offer to splice in
         // those features if not already defined by the static config
         if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
-            addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION);
-            addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS);
+            addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
+            addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
         }
 
         for (String featureName : mUnavailableFeatures) {
@@ -454,17 +455,21 @@
         }
     }
 
-    private void addFeature(String featureName) {
-        if (!mAvailableFeatures.containsKey(featureName)) {
-            final FeatureInfo fi = new FeatureInfo();
-            fi.name = featureName;
-            mAvailableFeatures.put(featureName, fi);
+    private void addFeature(String name, int version) {
+        FeatureInfo fi = mAvailableFeatures.get(name);
+        if (fi == null) {
+            fi = new FeatureInfo();
+            fi.name = name;
+            fi.version = version;
+            mAvailableFeatures.put(name, fi);
+        } else {
+            fi.version = Math.max(fi.version, version);
         }
     }
 
-    private void removeFeature(String featureName) {
-        if (mAvailableFeatures.remove(featureName) != null) {
-            Slog.d(TAG, "Removed unavailable feature " + featureName);
+    private void removeFeature(String name) {
+        if (mAvailableFeatures.remove(name) != null) {
+            Slog.d(TAG, "Removed unavailable feature " + name);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 9b5fde0..63c9408 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -577,7 +577,7 @@
             }
 
             // Android Wear Home
-            if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
                 Intent homeIntent = new Intent(Intent.ACTION_MAIN);
                 homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
 
@@ -612,7 +612,7 @@
             PackageParser.Package dialerPackage, int userId) {
         if (doesPackageSupportRuntimePermissions(dialerPackage)) {
             boolean isPhonePermFixed =
-                    mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+                    mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
             grantRuntimePermissionsLPw(
                     dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
             grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index caa58d6..cc44f70 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3503,9 +3503,14 @@
     }
 
     @Override
-    public boolean hasSystemFeature(String name) {
+    public boolean hasSystemFeature(String name, int version) {
         synchronized (mPackages) {
-            return mAvailableFeatures.containsKey(name);
+            final FeatureInfo feat = mAvailableFeatures.get(name);
+            if (feat == null) {
+                return false;
+            } else {
+                return feat.version >= version;
+            }
         }
     }
 
@@ -16909,15 +16914,22 @@
                 if (!checkin) {
                     pw.println("Features:");
                 }
-                Iterator<String> it = mAvailableFeatures.keySet().iterator();
-                while (it.hasNext()) {
-                    String name = it.next();
-                    if (!checkin) {
-                        pw.print("  ");
-                    } else {
+
+                for (FeatureInfo feat : mAvailableFeatures.values()) {
+                    if (checkin) {
                         pw.print("feat,");
+                        pw.print(feat.name);
+                        pw.print(",");
+                        pw.println(feat.version);
+                    } else {
+                        pw.print("  ");
+                        pw.print(feat.name);
+                        if (feat.version > 0) {
+                            pw.print(" version=");
+                            pw.print(feat.version);
+                        }
+                        pw.println();
                     }
-                    pw.println(name);
                 }
             }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d8845d8..d4048ef 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -339,9 +339,17 @@
         for (int p = 0; p < count; p++) {
             FeatureInfo fi = list.get(p);
             pw.print("feature:");
-            if (fi.name != null) pw.println(fi.name);
-            else pw.println("reqGlEsVersion=0x"
+            if (fi.name != null) {
+                pw.print(fi.name);
+                if (fi.version > 0) {
+                    pw.print("=");
+                    pw.print(fi.version);
+                }
+                pw.println();
+            } else {
+                pw.println("reqGlEsVersion=0x"
                     + Integer.toHexString(fi.reqGlEsVersion));
+            }
         }
         return 0;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cda60bd..1cd5635 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8136,7 +8136,7 @@
 
     private boolean hasFeatureManagedUsers() {
         try {
-            return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
+            return mIPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0);
         } catch (RemoteException e) {
             return false;
         }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 4e6d638..552ce6d 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -824,6 +824,11 @@
     }
 
     @Override
+    public boolean hasSystemFeature(String name, int version) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public boolean isSafeMode() {
         throw new UnsupportedOperationException();
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 5c20dfa..8da3cbd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -323,6 +323,11 @@
     }
 
     @Override
+    public boolean hasSystemFeature(String name, int version) {
+        return false;
+    }
+
+    @Override
     public ResolveInfo resolveActivity(Intent intent, int flags) {
         return null;
     }