diff options
| -rw-r--r-- | api/current.txt | 5 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 17 | ||||
| -rw-r--r-- | core/java/android/nfc/INfcCardEmulation.aidl | 2 | ||||
| -rw-r--r-- | core/java/android/nfc/NfcAdapter.java | 32 | ||||
| -rw-r--r-- | core/java/android/nfc/cardemulation/ApduServiceInfo.java | 70 | ||||
| -rw-r--r-- | core/java/android/nfc/cardemulation/CardEmulation.java | 103 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 2 |
7 files changed, 222 insertions, 9 deletions
diff --git a/api/current.txt b/api/current.txt index 95ac7c6e8412..541d0c43502b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11600,6 +11600,8 @@ package android.content.pm { field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc"; field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce"; field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef"; + field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese"; + field public static final java.lang.String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = "android.hardware.nfc.uicc"; field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep"; field public static final java.lang.String FEATURE_PC = "android.hardware.type.pc"; field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture"; @@ -30289,6 +30291,7 @@ package android.nfc { method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle); method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); + method public java.util.List<java.lang.String> getSupportedOffHostSecureElements(); method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler); method public boolean invokeBeam(android.app.Activity); method public boolean isEnabled(); @@ -30380,8 +30383,10 @@ package android.nfc.cardemulation { method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String); method public boolean registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>); method public boolean removeAidsForService(android.content.ComponentName, java.lang.String); + method public boolean setOffHostForService(android.content.ComponentName, java.lang.String); method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); method public boolean supportsAidPrefixRegistration(); + method public boolean unsetOffHostForService(android.content.ComponentName); method public boolean unsetPreferredService(android.app.Activity); field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; field public static final java.lang.String CATEGORY_OTHER = "other"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b72089fdb6f7..7ebe6ecbf502 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1939,6 +1939,23 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports uicc- + * based NFC card emulation. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC = + "android.hardware.nfc.uicc"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports eSE- + * based NFC card emulation. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE = "android.hardware.nfc.ese"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports any * one of the {@link #FEATURE_NFC}, {@link #FEATURE_NFC_HOST_CARD_EMULATION}, * or {@link #FEATURE_NFC_HOST_CARD_EMULATION_NFCF} features. diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl index d1b132c6abfa..dd2c0d4f9ee5 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -31,6 +31,8 @@ interface INfcCardEmulation boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category); boolean setDefaultForNextTap(int userHandle, in ComponentName service); boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup); + boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement); + boolean unsetOffHostForService(int userHandle, in ComponentName service); AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category); boolean removeAidGroupForService(int userHandle, in ComponentName service, String category); List<ApduServiceInfo> getServices(int userHandle, in String category); diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 21fed48189eb..877187053486 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -16,6 +16,7 @@ package android.nfc; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -42,7 +43,9 @@ import android.os.ServiceManager; import android.util.Log; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * Represents the local NFC adapter. @@ -488,6 +491,35 @@ public final class NfcAdapter { } /** + * Return list of Secure Elements which support off host card emulation. + * + * @return List<String> containing secure elements on the device which supports + * off host card emulation. eSE for Embedded secure element, + * SIM for UICC and so on. + */ + public @NonNull List<String> getSupportedOffHostSecureElements() { + List<String> offHostSE = new ArrayList<String>(); + IPackageManager pm = ActivityThread.getPackageManager(); + if (pm == null) { + Log.e(TAG, "Cannot get package manager, assuming no off-host CE feature"); + return offHostSE; + } + try { + if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_UICC, 0)) { + offHostSE.add("SIM"); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE, 0)) { + offHostSE.add("eSE"); + } + } catch (RemoteException e) { + Log.e(TAG, "Package manager query failed, assuming no off-host CE feature", e); + offHostSE.clear(); + return offHostSE; + } + return offHostSE; + } + + /** * Returns the NfcAdapter for application context, * or throws if NFC is not available. * @hide diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index e8d801c525e9..911ec8430ddd 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -18,11 +18,10 @@ package android.nfc.cardemulation; import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; -import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; @@ -30,7 +29,6 @@ import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; -import android.os.ResultReceiver; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -69,6 +67,18 @@ public final class ApduServiceInfo implements Parcelable { final boolean mOnHost; /** + * Offhost reader name. + * eg: SIM, eSE etc + */ + String mOffHostName; + + /** + * Offhost reader name from manifest file. + * Used for unsetOffHostSecureElement() + */ + final String mStaticOffHostName; + + /** * Mapping from category to static AID group */ @UnsupportedAppUsage @@ -104,15 +114,17 @@ public final class ApduServiceInfo implements Parcelable { * @hide */ @UnsupportedAppUsage - public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, + public ApduServiceInfo(ResolveInfo info, String description, ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, - String settingsActivityName) { + String settingsActivityName, String offHost, String staticOffHost) { this.mService = info; this.mDescription = description; this.mStaticAidGroups = new HashMap<String, AidGroup>(); this.mDynamicAidGroups = new HashMap<String, AidGroup>(); - this.mOnHost = onHost; + this.mOffHostName = offHost; + this.mStaticOffHostName = staticOffHost; + this.mOnHost = (offHost == null); this.mRequiresDeviceUnlock = requiresUnlock; for (AidGroup aidGroup : staticAidGroups) { this.mStaticAidGroups.put(aidGroup.category, aidGroup); @@ -174,6 +186,8 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); mSettingsActivityName = sa.getString( com.android.internal.R.styleable.HostApduService_settingsActivity); + mOffHostName = null; + mStaticOffHostName = mOffHostName; sa.recycle(); } else { TypedArray sa = res.obtainAttributes(attrs, @@ -186,6 +200,16 @@ public final class ApduServiceInfo implements Parcelable { com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); mSettingsActivityName = sa.getString( com.android.internal.R.styleable.HostApduService_settingsActivity); + mOffHostName = sa.getString( + com.android.internal.R.styleable.OffHostApduService_secureElementName); + if (mOffHostName != null) { + if (mOffHostName.equals("eSE")) { + mOffHostName = "eSE1"; + } else if (mOffHostName.equals("SIM")) { + mOffHostName = "SIM1"; + } + } + mStaticOffHostName = mOffHostName; sa.recycle(); } @@ -289,6 +313,10 @@ public final class ApduServiceInfo implements Parcelable { mService.serviceInfo.name); } + public String getOffHostSecureElement() { + return mOffHostName; + } + /** * Returns a consolidated list of AIDs from the AID groups * registered by this service. Note that if a service has both @@ -404,6 +432,20 @@ public final class ApduServiceInfo implements Parcelable { mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); } + @UnsupportedAppUsage + public void setOffHostSecureElement(String offHost) { + mOffHostName = offHost; + } + + /** + * Resets the off host Secure Element to statically defined + * by the service in the manifest file. + */ + @UnsupportedAppUsage + public void unsetOffHostSecureElement() { + mOffHostName = mStaticOffHostName; + } + public CharSequence loadLabel(PackageManager pm) { return mService.loadLabel(pm); } @@ -481,6 +523,8 @@ public final class ApduServiceInfo implements Parcelable { mService.writeToParcel(dest, flags); dest.writeString(mDescription); dest.writeInt(mOnHost ? 1 : 0); + dest.writeString(mOffHostName); + dest.writeString(mStaticOffHostName); dest.writeInt(mStaticAidGroups.size()); if (mStaticAidGroups.size() > 0) { dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values())); @@ -503,6 +547,8 @@ public final class ApduServiceInfo implements Parcelable { ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); String description = source.readString(); boolean onHost = source.readInt() != 0; + String offHostName = source.readString(); + String staticOffHostName = source.readString(); ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>(); int numStaticGroups = source.readInt(); if (numStaticGroups > 0) { @@ -517,9 +563,9 @@ public final class ApduServiceInfo implements Parcelable { int bannerResource = source.readInt(); int uid = source.readInt(); String settingsActivityName = source.readString(); - return new ApduServiceInfo(info, onHost, description, staticAidGroups, + return new ApduServiceInfo(info, description, staticAidGroups, dynamicAidGroups, requiresUnlock, bannerResource, uid, - settingsActivityName); + settingsActivityName, offHostName, staticOffHostName); } @Override @@ -531,6 +577,14 @@ public final class ApduServiceInfo implements Parcelable { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(" " + getComponent() + " (Description: " + getDescription() + ")"); + if (mOnHost) { + pw.println(" On Host Service"); + } else { + pw.println(" Off-host Service"); + pw.println(" " + "Current off-host SE" + mOffHostName + + " static off-host: " + mOffHostName); + } + pw.println(" Static off-host Secure Element:"); pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { pw.println(" Category: " + group.category); diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 15d02f2e5ddf..01932abfaa9b 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -27,7 +27,6 @@ import android.content.pm.PackageManager; import android.nfc.INfcCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; -import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; @@ -345,6 +344,108 @@ public final class CardEmulation { } /** + * Unsets the off-host Secure Element for the given service. + * + * <p>Note that this will only remove Secure Element that was dynamically + * set using the {@link #setOffHostForService(ComponentName, String)} + * and resets it to a value that was statically assigned using manifest. + * + * <p>Note that you can only unset off-host SE for a service that + * is running under the same UID as the caller of this API. Typically + * this means you need to call this from the same + * package as the service itself, though UIDs can also + * be shared between packages using shared UIDs. + * + * @param service The component name of the service + * @return whether the registration was successful. + */ + public boolean unsetOffHostForService(ComponentName service) { + NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); + if (adapter == null) { + return false; + } + + try { + return sService.unsetOffHostForService(mContext.getUserId(), service); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.unsetOffHostForService(mContext.getUserId(), service); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * Sets the off-host Secure Element for the given service. + * + * <p>If off-host SE was initially set (either statically + * through the manifest, or dynamically by using this API), + * it will be replaced with this one. All AIDs registered by + * this service will be re-routed to this Secure Element if + * successful. + * + * <p>Note that you can only set off-host SE for a service that + * is running under the same UID as the caller of this API. Typically + * this means you need to call this from the same + * package as the service itself, though UIDs can also + * be shared between packages using shared UIDs. + * + * <p>Registeration will be successful only if the Secure Element + * exists on the device. + * + * @param service The component name of the service + * @param offHostSecureElement Secure Element to register the AID to + * @return whether the registration was successful. + */ + public boolean setOffHostForService(ComponentName service, String offHostSecureElement) { + boolean validSecureElement = false; + + NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext); + if (adapter == null || offHostSecureElement == null) { + return false; + } + + List<String> validSE = adapter.getSupportedOffHostSecureElements(); + if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE")) + || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) { + return false; + } + + if (offHostSecureElement.equals("eSE")) { + offHostSecureElement = "eSE1"; + } else if (offHostSecureElement.equals("SIM")) { + offHostSecureElement = "SIM1"; + } + + try { + return sService.setOffHostForService(mContext.getUserId(), service, + offHostSecureElement); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setOffHostForService(mContext.getUserId(), service, + offHostSecureElement); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** * Retrieves the currently registered AIDs for the specified * category for a service. * diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 91faa5517860..183c2e8cf486 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3755,6 +3755,8 @@ <!-- Component name of an activity that allows the user to modify the settings for this service. --> <attr name="settingsActivity"/> + <!-- Secure Element which the AIDs should be routed to --> + <attr name="secureElementName"/> </declare-styleable> <!-- Specify one or more <code>aid-group</code> elements inside a |