/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.nfc.cardemulation; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; 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.util.Log; import java.util.HashMap; import java.util.List; public final class CardEmulation { static final String TAG = "CardEmulation"; /** * Activity action: ask the user to change the default * card emulation service for a certain category. This will * show a dialog that asks the user whether he wants to * replace the current default service with the service * identified with the ComponentName specified in * {@link #EXTRA_SERVICE_COMPONENT}, for the category * specified in {@link #EXTRA_CATEGORY} */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; /** * The category extra for {@link #ACTION_CHANGE_DEFAULT} * * @see #ACTION_CHANGE_DEFAULT */ public static final String EXTRA_CATEGORY = "category"; /** * The ComponentName object passed in as a parcelable * extra for {@link #ACTION_CHANGE_DEFAULT} * * @see #ACTION_CHANGE_DEFAULT */ public static final String EXTRA_SERVICE_COMPONENT = "component"; /** * The payment category can be used to indicate that an AID * represents a payment application. */ public static final String CATEGORY_PAYMENT = "payment"; /** * If an AID group does not contain a category, or the * specified category is not defined by the platform version * that is parsing the AID group, all AIDs in the group will * automatically be categorized under the {@link #CATEGORY_OTHER} * category. */ public static final String CATEGORY_OTHER = "other"; /** * Return value for {@link #getSelectionModeForCategory(String)}. * *
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. * *
There are still cases where a service that is * not the default for a category can selected: *
* 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. *
* 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. *
Example: *
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)}. * *
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 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.
*/
public boolean isDefaultServiceForCategory(ComponentName service, String category) {
try {
return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
} 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);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
}
}
/**
*
* Allows an application to query whether a service is currently
* the default handler for a specified ISO7816-4 Application ID.
*
* @param service The ComponentName of the service
* @param aid The ISO7816-4 Application ID
* @return
*/
public boolean isDefaultServiceForAid(ComponentName service, String aid) {
try {
return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
}
}
}
/**
* Returns the application selection mode for the passed in category.
* Valid return values are:
* {@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
* application for this category, which will be preferred.
* {@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
* every time what app he would like to use in this category.
* {@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
*/
public boolean setDefaultServiceForCategory(ComponentName service, String category) {
try {
return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
category);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
}
}
}
/**
* @hide
*/
public boolean setDefaultForNextTap(ComponentName service) {
try {
return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
} catch (RemoteException e) {
// Try one more time
recoverService();
if (sService == null) {
Log.e(TAG, "Failed to recover CardEmulationService.");
return false;
}
try {
return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
}
}
}
/**
* @hide
*/
public List