Home | History | Annotate | Download | only in cardemulation
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.nfc.cardemulation;
     18 
     19 import android.annotation.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.app.ActivityThread;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.pm.IPackageManager;
     25 import android.content.pm.PackageManager;
     26 import android.nfc.INfcCardEmulation;
     27 import android.nfc.NfcAdapter;
     28 import android.os.RemoteException;
     29 import android.os.UserHandle;
     30 import android.provider.Settings;
     31 import android.util.Log;
     32 
     33 import java.util.HashMap;
     34 import java.util.List;
     35 
     36 /**
     37  * This class can be used to query the state of
     38  * NFC card emulation services.
     39  *
     40  * For a general introduction into NFC card emulation,
     41  * please read the <a href="{@docRoot}guide/topics/nfc/ce.html">
     42  * NFC card emulation developer guide</a>.</p>
     43  *
     44  * <p class="note">Use of this class requires the
     45  * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
     46  * on the device.
     47  */
     48 public final class CardEmulation {
     49     static final String TAG = "CardEmulation";
     50 
     51     /**
     52      * Activity action: ask the user to change the default
     53      * card emulation service for a certain category. This will
     54      * show a dialog that asks the user whether he wants to
     55      * replace the current default service with the service
     56      * identified with the ComponentName specified in
     57      * {@link #EXTRA_SERVICE_COMPONENT}, for the category
     58      * specified in {@link #EXTRA_CATEGORY}
     59      */
     60     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     61     public static final String ACTION_CHANGE_DEFAULT =
     62             "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
     63 
     64     /**
     65      * The category extra for {@link #ACTION_CHANGE_DEFAULT}.
     66      *
     67      * @see #ACTION_CHANGE_DEFAULT
     68      */
     69     public static final String EXTRA_CATEGORY = "category";
     70 
     71     /**
     72      * The service {@link ComponentName} object passed in as an
     73      * extra for {@link #ACTION_CHANGE_DEFAULT}.
     74      *
     75      * @see #ACTION_CHANGE_DEFAULT
     76      */
     77     public static final String EXTRA_SERVICE_COMPONENT = "component";
     78 
     79     /**
     80      * Category used for NFC payment services.
     81      */
     82     public static final String CATEGORY_PAYMENT = "payment";
     83 
     84     /**
     85      * Category that can be used for all other card emulation
     86      * services.
     87      */
     88     public static final String CATEGORY_OTHER = "other";
     89 
     90     /**
     91      * Return value for {@link #getSelectionModeForCategory(String)}.
     92      *
     93      * <p>In this mode, the user has set a default service for this
     94      *    category.
     95      *
     96      * <p>When using ISO-DEP card emulation with {@link HostApduService}
     97      *    or {@link OffHostApduService}, if a remote NFC device selects
     98      *    any of the Application IDs (AIDs)
     99      *    that the default service has registered in this category,
    100      *    that service will automatically be bound to to handle
    101      *    the transaction.
    102      */
    103     public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
    104 
    105     /**
    106      * Return value for {@link #getSelectionModeForCategory(String)}.
    107      *
    108      * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
    109      *    or {@link OffHostApduService}, whenever an Application ID (AID) of this category
    110      *    is selected, the user is asked which service he wants to use to handle
    111      *    the transaction, even if there is only one matching service.
    112      */
    113     public static final int SELECTION_MODE_ALWAYS_ASK = 1;
    114 
    115     /**
    116      * Return value for {@link #getSelectionModeForCategory(String)}.
    117      *
    118      * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
    119      *    or {@link OffHostApduService}, the user will only be asked to select a service
    120      *    if the Application ID (AID) selected by the reader has been registered by multiple
    121      *    services. If there is only one service that has registered for the AID,
    122      *    that service will be invoked directly.
    123      */
    124     public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
    125 
    126     static boolean sIsInitialized = false;
    127     static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
    128     static INfcCardEmulation sService;
    129 
    130     final Context mContext;
    131 
    132     private CardEmulation(Context context, INfcCardEmulation service) {
    133         mContext = context.getApplicationContext();
    134         sService = service;
    135     }
    136 
    137     /**
    138      * Helper to get an instance of this class.
    139      *
    140      * @param adapter A reference to an NfcAdapter object.
    141      * @return
    142      */
    143     public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
    144         if (adapter == null) throw new NullPointerException("NfcAdapter is null");
    145         Context context = adapter.getContext();
    146         if (context == null) {
    147             Log.e(TAG, "NfcAdapter context is null.");
    148             throw new UnsupportedOperationException();
    149         }
    150         if (!sIsInitialized) {
    151             IPackageManager pm = ActivityThread.getPackageManager();
    152             if (pm == null) {
    153                 Log.e(TAG, "Cannot get PackageManager");
    154                 throw new UnsupportedOperationException();
    155             }
    156             try {
    157                 if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
    158                     Log.e(TAG, "This device does not support card emulation");
    159                     throw new UnsupportedOperationException();
    160                 }
    161             } catch (RemoteException e) {
    162                 Log.e(TAG, "PackageManager query failed.");
    163                 throw new UnsupportedOperationException();
    164             }
    165             sIsInitialized = true;
    166         }
    167         CardEmulation manager = sCardEmus.get(context);
    168         if (manager == null) {
    169             // Get card emu service
    170             INfcCardEmulation service = adapter.getCardEmulationService();
    171             manager = new CardEmulation(context, service);
    172             sCardEmus.put(context, manager);
    173         }
    174         return manager;
    175     }
    176 
    177     /**
    178      * Allows an application to query whether a service is currently
    179      * the default service to handle a card emulation category.
    180      *
    181      * <p>Note that if {@link #getSelectionModeForCategory(String)}
    182      * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT},
    183      * this method will always return false. That is because in these
    184      * selection modes a default can't be set at the category level. For categories where
    185      * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or
    186      * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use
    187      * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service
    188      * is the default for a specific AID.
    189      *
    190      * @param service The ComponentName of the service
    191      * @param category The category
    192      * @return whether service is currently the default service for the category.
    193      *
    194      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    195      */
    196     public boolean isDefaultServiceForCategory(ComponentName service, String category) {
    197         try {
    198             return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
    199         } catch (RemoteException e) {
    200             // Try one more time
    201             recoverService();
    202             if (sService == null) {
    203                 Log.e(TAG, "Failed to recover CardEmulationService.");
    204                 return false;
    205             }
    206             try {
    207                 return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
    208                         category);
    209             } catch (RemoteException ee) {
    210                 Log.e(TAG, "Failed to recover CardEmulationService.");
    211                 return false;
    212             }
    213         }
    214     }
    215 
    216     /**
    217      *
    218      * Allows an application to query whether a service is currently
    219      * the default handler for a specified ISO7816-4 Application ID.
    220      *
    221      * @param service The ComponentName of the service
    222      * @param aid The ISO7816-4 Application ID
    223      * @return whether the service is the default handler for the specified AID
    224      *
    225      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    226      */
    227     public boolean isDefaultServiceForAid(ComponentName service, String aid) {
    228         try {
    229             return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
    230         } catch (RemoteException e) {
    231             // Try one more time
    232             recoverService();
    233             if (sService == null) {
    234                 Log.e(TAG, "Failed to recover CardEmulationService.");
    235                 return false;
    236             }
    237             try {
    238                 return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
    239             } catch (RemoteException ee) {
    240                 Log.e(TAG, "Failed to reach CardEmulationService.");
    241                 return false;
    242             }
    243         }
    244     }
    245 
    246     /**
    247      * Returns the service selection mode for the passed in category.
    248      * Valid return values are:
    249      * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
    250      *    service for this category, which will be preferred.
    251      * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
    252      *    every time what service he would like to use in this category.
    253      * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
    254      *    to pick a service if there is a conflict.
    255      * @param category The category, for example {@link #CATEGORY_PAYMENT}
    256      * @return the selection mode for the passed in category
    257      */
    258     public int getSelectionModeForCategory(String category) {
    259         if (CATEGORY_PAYMENT.equals(category)) {
    260             String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
    261                     Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
    262             if (defaultComponent != null) {
    263                 return SELECTION_MODE_PREFER_DEFAULT;
    264             } else {
    265                 return SELECTION_MODE_ALWAYS_ASK;
    266             }
    267         } else {
    268             // All other categories are in "only ask if conflict" mode
    269             return SELECTION_MODE_ASK_IF_CONFLICT;
    270         }
    271     }
    272 
    273     /**
    274      * @hide
    275      */
    276     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
    277         try {
    278             return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
    279         } catch (RemoteException e) {
    280             // Try one more time
    281             recoverService();
    282             if (sService == null) {
    283                 Log.e(TAG, "Failed to recover CardEmulationService.");
    284                 return false;
    285             }
    286             try {
    287                 return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
    288                         category);
    289             } catch (RemoteException ee) {
    290                 Log.e(TAG, "Failed to reach CardEmulationService.");
    291                 return false;
    292             }
    293         }
    294     }
    295 
    296     /**
    297      * @hide
    298      */
    299     public boolean setDefaultForNextTap(ComponentName service) {
    300         try {
    301             return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
    302         } catch (RemoteException e) {
    303             // Try one more time
    304             recoverService();
    305             if (sService == null) {
    306                 Log.e(TAG, "Failed to recover CardEmulationService.");
    307                 return false;
    308             }
    309             try {
    310                 return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
    311             } catch (RemoteException ee) {
    312                 Log.e(TAG, "Failed to reach CardEmulationService.");
    313                 return false;
    314             }
    315         }
    316     }
    317 
    318     /**
    319      * @hide
    320      */
    321     public List<ApduServiceInfo> getServices(String category) {
    322         try {
    323             return sService.getServices(UserHandle.myUserId(), category);
    324         } catch (RemoteException e) {
    325             // Try one more time
    326             recoverService();
    327             if (sService == null) {
    328                 Log.e(TAG, "Failed to recover CardEmulationService.");
    329                 return null;
    330             }
    331             try {
    332                 return sService.getServices(UserHandle.myUserId(), category);
    333             } catch (RemoteException ee) {
    334                 Log.e(TAG, "Failed to reach CardEmulationService.");
    335                 return null;
    336             }
    337         }
    338     }
    339 
    340     void recoverService() {
    341         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
    342         sService = adapter.getCardEmulationService();
    343     }
    344 }
    345