From 115d2c189a46f535778d9dd0923f703ff2f888fe Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 15 Feb 2016 17:25:57 -0700 Subject: [PATCH] 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 --- api/current.txt | 4 ++ api/system-current.txt | 4 ++ api/test-current.txt | 4 ++ .../com/android/commands/svc/NfcCommand.java | 2 +- .../app/ApplicationPackageManager.java | 7 +++- core/java/android/content/pm/FeatureInfo.java | 40 +++++++++++++++---- .../android/content/pm/IPackageManager.aidl | 2 +- .../android/content/pm/PackageManager.java | 20 ++++++++-- .../android/content/pm/PackageParser.java | 2 + core/java/android/nfc/NfcAdapter.java | 2 +- .../nfc/cardemulation/CardEmulation.java | 2 +- .../nfc/cardemulation/NfcFCardEmulation.java | 2 +- core/res/res/values/attrs_manifest.xml | 18 +++++---- core/res/res/values/public.xml | 1 + .../statusbar/phone/PhoneStatusBar.java | 2 +- .../java/com/android/server/SystemConfig.java | 27 ++++++++----- .../pm/DefaultPermissionGrantPolicy.java | 4 +- .../server/pm/PackageManagerService.java | 30 +++++++++----- .../server/pm/PackageManagerShellCommand.java | 12 +++++- .../DevicePolicyManagerService.java | 2 +- .../android/test/mock/MockPackageManager.java | 5 +++ .../bridge/android/BridgePackageManager.java | 5 +++ 22 files changed, 146 insertions(+), 51 deletions(-) diff --git a/api/current.txt b/api/current.txt index 3c0c64df89ba9..0337e11a3c79b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1376,6 +1376,7 @@ package android { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 queryBroadcastReceivers(android.content.Intent, int); @@ -37789,6 +37792,7 @@ package android.test.mock { 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 queryBroadcastReceivers(android.content.Intent, int); diff --git a/api/system-current.txt b/api/system-current.txt index 335557b4baf9a..266900f917e97 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1475,6 +1475,7 @@ package android { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 queryBroadcastReceivers(android.content.Intent, int); @@ -40538,6 +40541,7 @@ package android.test.mock { 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 queryBroadcastReceivers(android.content.Intent, int); diff --git a/api/test-current.txt b/api/test-current.txt index 6bd4887582edc..15b1e7884cf50 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1376,6 +1376,7 @@ package android { 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 @@ package android.content.pm { 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 @@ package android.content.pm { 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 queryBroadcastReceivers(android.content.Intent, int); @@ -37806,6 +37809,7 @@ package android.test.mock { 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 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 e0f09ee2c6666..8e9791f8b731f 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 @@ public class NfcCommand extends Svc.Command { 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 df4b7d1eb50d8..7e50518cb3e12 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -450,8 +450,13 @@ public class ApplicationPackageManager extends PackageManager { @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 79fa32791a31f..7671f72cbeacc 100644 --- a/core/java/android/content/pm/FeatureInfo.java +++ b/core/java/android/content/pm/FeatureInfo.java @@ -20,9 +20,18 @@ import android.os.Parcel; 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 } tag. + * Definition of a single optional hardware or software feature of an Android + * device. + *

+ * 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. + *

+ * 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 @@ public class FeatureInfo implements Parcelable { * 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. + *

+ * 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 class FeatureInfo implements Parcelable { 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 @@ public class FeatureInfo implements Parcelable { } } + @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 CREATOR = - new Creator() { + public static final Creator CREATOR = new Creator() { + @Override public FeatureInfo createFromParcel(Parcel source) { return new FeatureInfo(source); } + @Override public FeatureInfo[] newArray(int size) { return new FeatureInfo[size]; } @@ -98,6 +123,7 @@ public class FeatureInfo implements Parcelable { 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 386385790e49e..c71a60353e1ad 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -416,7 +416,7 @@ interface IPackageManager { */ 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 bf0d4ded17a9e..36b902c3e481a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3405,14 +3405,26 @@ public abstract class PackageManager { 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 any 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 diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5ae8d4cf0ce41..1ee19de282acf 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2127,6 +2127,8 @@ public class PackageParser { // 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 acd780d9ecad3..6f911ceb4bedd 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -414,7 +414,7 @@ public final class NfcAdapter { 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 23d05bda36333..b49288e6e59ef 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -156,7 +156,7 @@ public final class CardEmulation { 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 d61ac02eb98e5..42ccf20a584a5 100644 --- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java +++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java @@ -77,7 +77,7 @@ public final class NfcFCardEmulation { 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 1496d0926dc6d..f91bcd0bdeeaa 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1425,22 +1425,24 @@ - + + + + - - - +