am a6dcf1a2: Merge "Add an API for querying HCE service selection mode." into klp-dev
* commit 'a6dcf1a2d634bd8c26b6a5744fc38874bcac6dd9': Add an API for querying HCE service selection mode.
This commit is contained in:
@@ -15261,6 +15261,7 @@ package android.nfc.cardemulation {
|
||||
|
||||
public final class CardEmulationManager {
|
||||
method public static synchronized android.nfc.cardemulation.CardEmulationManager getInstance(android.nfc.NfcAdapter);
|
||||
method public int getSelectionModeForCategory(java.lang.String);
|
||||
method public boolean isDefaultServiceForAid(android.content.ComponentName, java.lang.String);
|
||||
method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
|
||||
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.ACTION_CHANGE_DEFAULT";
|
||||
@@ -15268,6 +15269,9 @@ package android.nfc.cardemulation {
|
||||
field public static final java.lang.String CATEGORY_PAYMENT = "payment";
|
||||
field public static final java.lang.String EXTRA_CATEGORY = "category";
|
||||
field public static final java.lang.String EXTRA_SERVICE_COMPONENT = "component";
|
||||
field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
|
||||
field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
|
||||
field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
|
||||
}
|
||||
|
||||
public abstract class HostApduService extends android.app.Service {
|
||||
|
||||
@@ -74,17 +74,23 @@ public final class ApduServiceInfo implements Parcelable {
|
||||
*/
|
||||
final HashMap<String, AidGroup> mCategoryToGroup;
|
||||
|
||||
/**
|
||||
* Whether this service should only be started when the device is unlocked.
|
||||
*/
|
||||
final boolean mRequiresDeviceUnlock;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
|
||||
ArrayList<AidGroup> aidGroups) {
|
||||
ArrayList<AidGroup> aidGroups, boolean requiresUnlock) {
|
||||
this.mService = info;
|
||||
this.mDescription = description;
|
||||
this.mAidGroups = aidGroups;
|
||||
this.mAids = new ArrayList<String>();
|
||||
this.mCategoryToGroup = new HashMap<String, AidGroup>();
|
||||
this.mOnHost = onHost;
|
||||
this.mRequiresDeviceUnlock = requiresUnlock;
|
||||
for (AidGroup aidGroup : aidGroups) {
|
||||
this.mCategoryToGroup.put(aidGroup.category, aidGroup);
|
||||
this.mAids.addAll(aidGroup.aids);
|
||||
@@ -132,12 +138,16 @@ public final class ApduServiceInfo implements Parcelable {
|
||||
mService = info;
|
||||
mDescription = sa.getString(
|
||||
com.android.internal.R.styleable.HostApduService_description);
|
||||
mRequiresDeviceUnlock = sa.getBoolean(
|
||||
com.android.internal.R.styleable.HostApduService_requireDeviceUnlock,
|
||||
false);
|
||||
} else {
|
||||
TypedArray sa = res.obtainAttributes(attrs,
|
||||
com.android.internal.R.styleable.OffHostApduService);
|
||||
mService = info;
|
||||
mDescription = sa.getString(
|
||||
com.android.internal.R.styleable.OffHostApduService_description);
|
||||
mRequiresDeviceUnlock = false;
|
||||
}
|
||||
|
||||
mAidGroups = new ArrayList<AidGroup>();
|
||||
@@ -226,6 +236,10 @@ public final class ApduServiceInfo implements Parcelable {
|
||||
return mOnHost;
|
||||
}
|
||||
|
||||
public boolean requiresUnlock() {
|
||||
return mRequiresDeviceUnlock;
|
||||
}
|
||||
|
||||
public CharSequence loadLabel(PackageManager pm) {
|
||||
return mService.loadLabel(pm);
|
||||
}
|
||||
@@ -287,6 +301,7 @@ public final class ApduServiceInfo implements Parcelable {
|
||||
if (mAidGroups.size() > 0) {
|
||||
dest.writeTypedList(mAidGroups);
|
||||
}
|
||||
dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
|
||||
};
|
||||
|
||||
public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
|
||||
@@ -301,7 +316,8 @@ public final class ApduServiceInfo implements Parcelable {
|
||||
if (numGroups > 0) {
|
||||
source.readTypedList(aidGroups, AidGroup.CREATOR);
|
||||
}
|
||||
return new ApduServiceInfo(info, onHost, description, aidGroups);
|
||||
boolean requiresUnlock = (source.readInt() != 0) ? true : false;
|
||||
return new ApduServiceInfo(info, onHost, description, aidGroups, requiresUnlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.nfc.INfcCardEmulation;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -78,20 +79,69 @@ public final class CardEmulationManager {
|
||||
*/
|
||||
public static final String CATEGORY_OTHER = "other";
|
||||
|
||||
/**
|
||||
* Return value for {@link #getSelectionModeForCategory(String)}.
|
||||
*
|
||||
* <p>In this mode, the user has set a default service for this
|
||||
* AID category. If a remote reader selects any of the AIDs
|
||||
* that the default service has registered in this category,
|
||||
* that service will automatically be bound to to handle
|
||||
* the transaction.
|
||||
*
|
||||
* <p>There are still cases where a service that is
|
||||
* not the default for a category can selected:
|
||||
* <p>
|
||||
* If a remote reader selects an AID in this category
|
||||
* that is not handled by the default service, and there is a set
|
||||
* of other services {S} that do handle this AID, the
|
||||
* user is asked if he wants to use any of the services in
|
||||
* {S} instead.
|
||||
* <p>
|
||||
* As a special case, if the size of {S} is one, containing a single service X,
|
||||
* and all AIDs X has registered in this category are not
|
||||
* registered by any other service, then X will be
|
||||
* selected automatically without asking the user.
|
||||
* <p>Example:
|
||||
* <ul>
|
||||
* <li>Service A registers AIDs "1", "2" and "3" in the category
|
||||
* <li>Service B registers AIDs "3" and "4" in the category
|
||||
* <li>Service C registers AIDs "5" and "6" in the category
|
||||
* </ul>
|
||||
* In this case, the following will happen when service A
|
||||
* is the default:
|
||||
* <ul>
|
||||
* <li>Reader selects AID "1", "2" or "3": service A is invoked automatically
|
||||
* <li>Reader selects AID "4": the user is asked to confirm he
|
||||
* wants to use service B, because its AIDs overlap with service A.
|
||||
* <li>Reader selects AID "5" or "6": service C is invoked automatically,
|
||||
* because all AIDs it has asked for are only registered by C,
|
||||
* and there is no overlap.
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
|
||||
|
||||
/**
|
||||
* Return value for {@link #getSelectionModeForCategory(String)}.
|
||||
*
|
||||
* <p>In this mode, whenever an AID of this category is selected,
|
||||
* the user is asked which service he wants to use to handle
|
||||
* the transaction, even if there is only one matching service.
|
||||
*/
|
||||
public static final int SELECTION_MODE_ALWAYS_ASK = 1;
|
||||
|
||||
/**
|
||||
* Return value for {@link #getSelectionModeForCategory(String)}.
|
||||
*
|
||||
* <p>In this mode, the user will only be asked to select a service
|
||||
* if the selected AID has been registered by multiple applications.
|
||||
*/
|
||||
public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
|
||||
|
||||
static boolean sIsInitialized = false;
|
||||
static HashMap<Context, CardEmulationManager> sCardEmuManagers = new HashMap();
|
||||
static INfcCardEmulation sService;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String PAYMENT_MODE_AUTO = "auto";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String PAYMENT_MODE_MANUAL = "manual";
|
||||
|
||||
final Context mContext;
|
||||
|
||||
private CardEmulationManager(Context context, INfcCardEmulation service) {
|
||||
@@ -113,7 +163,7 @@ public final class CardEmulationManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
try {
|
||||
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HCE)) {
|
||||
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
|
||||
Log.e(TAG, "This device does not support card emulation");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@@ -137,6 +187,10 @@ public final class CardEmulationManager {
|
||||
* Allows an application to query whether a service is currently
|
||||
* the default service to handle a card emulation category.
|
||||
*
|
||||
* <p>Note that if {@link #getSelectionModeForCategory(String)}
|
||||
* returns {@link #SELECTION_MODE_ALWAYS_ASK}, this method will always
|
||||
* return false.
|
||||
*
|
||||
* @param service The ComponentName of the service
|
||||
* @param category The category
|
||||
* @return whether service is currently the default service for the category.
|
||||
@@ -147,6 +201,10 @@ public final class CardEmulationManager {
|
||||
} catch (RemoteException e) {
|
||||
// Try one more time
|
||||
recoverService();
|
||||
if (sService == null) {
|
||||
Log.e(TAG, "Failed to recover CardEmulationService.");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
|
||||
category);
|
||||
@@ -185,6 +243,33 @@ public final class CardEmulationManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application selection mode for the passed in category.
|
||||
* Valid return values are:
|
||||
* <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
|
||||
* application for this category, which will be preferred.
|
||||
* <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
|
||||
* every time what app he would like to use in this category.
|
||||
* <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
|
||||
* to pick a service if there is a conflict.
|
||||
* @param category The category, for example {@link #CATEGORY_PAYMENT}
|
||||
* @return
|
||||
*/
|
||||
public int getSelectionModeForCategory(String category) {
|
||||
if (CATEGORY_PAYMENT.equals(category)) {
|
||||
String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
|
||||
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
|
||||
if (defaultComponent != null) {
|
||||
return SELECTION_MODE_PREFER_DEFAULT;
|
||||
} else {
|
||||
return SELECTION_MODE_ALWAYS_ASK;
|
||||
}
|
||||
} else {
|
||||
// All other categories are in "only ask if conflict" mode
|
||||
return SELECTION_MODE_ASK_IF_CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -4,13 +4,11 @@ import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
@@ -4284,12 +4284,6 @@ public final class Settings {
|
||||
*/
|
||||
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
|
||||
|
||||
/**
|
||||
* Whether to automatically invoke NFC payment app or manually select on tap.
|
||||
* @hide
|
||||
*/
|
||||
public static final String NFC_PAYMENT_MODE = "nfc_payment_mode";
|
||||
|
||||
/**
|
||||
* Name of a package that the current user has explicitly allowed to see all of that
|
||||
* user's notifications.
|
||||
|
||||
Reference in New Issue
Block a user