Merge changes from topic "multi-se-support"

* changes:
  Add a mechanism to register AIDs to specific off-host SE
  Add Off-Host Card Emulation Features
This commit is contained in:
Ruchi Kandoi
2019-01-04 19:56:41 +00:00
committed by Gerrit Code Review
7 changed files with 222 additions and 9 deletions

View File

@@ -11317,6 +11317,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";
@@ -29165,6 +29167,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();
@@ -29256,8 +29259,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";

View File

@@ -1912,6 +1912,23 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_NFC_HOST_CARD_EMULATION_NFCF = "android.hardware.nfc.hcef";
/**
* 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

View File

@@ -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);

View File

@@ -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.
@@ -487,6 +490,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.

View File

@@ -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;
@@ -68,6 +66,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
*/
@@ -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);

View File

@@ -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;
@@ -344,6 +343,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.

View File

@@ -3690,6 +3690,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