• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Activity;
22 import android.app.ActivityThread;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.pm.IPackageManager;
26 import android.content.pm.PackageManager;
27 import android.nfc.INfcCardEmulation;
28 import android.nfc.NfcAdapter;
29 import android.os.RemoteException;
30 import android.os.UserHandle;
31 import android.provider.Settings;
32 import android.provider.Settings.SettingNotFoundException;
33 import android.util.Log;
34 
35 import java.util.HashMap;
36 import java.util.List;
37 
38 /**
39  * This class can be used to query the state of
40  * NFC card emulation services.
41  *
42  * For a general introduction into NFC card emulation,
43  * please read the <a href="{@docRoot}guide/topics/connectivity/nfc/hce.html">
44  * NFC card emulation developer guide</a>.</p>
45  *
46  * <p class="note">Use of this class requires the
47  * {@link PackageManager#FEATURE_NFC_HOST_CARD_EMULATION} to be present
48  * on the device.
49  */
50 public final class CardEmulation {
51     static final String TAG = "CardEmulation";
52 
53     /**
54      * Activity action: ask the user to change the default
55      * card emulation service for a certain category. This will
56      * show a dialog that asks the user whether he wants to
57      * replace the current default service with the service
58      * identified with the ComponentName specified in
59      * {@link #EXTRA_SERVICE_COMPONENT}, for the category
60      * specified in {@link #EXTRA_CATEGORY}
61      */
62     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
63     public static final String ACTION_CHANGE_DEFAULT =
64             "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
65 
66     /**
67      * The category extra for {@link #ACTION_CHANGE_DEFAULT}.
68      *
69      * @see #ACTION_CHANGE_DEFAULT
70      */
71     public static final String EXTRA_CATEGORY = "category";
72 
73     /**
74      * The service {@link ComponentName} object passed in as an
75      * extra for {@link #ACTION_CHANGE_DEFAULT}.
76      *
77      * @see #ACTION_CHANGE_DEFAULT
78      */
79     public static final String EXTRA_SERVICE_COMPONENT = "component";
80 
81     /**
82      * Category used for NFC payment services.
83      */
84     public static final String CATEGORY_PAYMENT = "payment";
85 
86     /**
87      * Category that can be used for all other card emulation
88      * services.
89      */
90     public static final String CATEGORY_OTHER = "other";
91 
92     /**
93      * Return value for {@link #getSelectionModeForCategory(String)}.
94      *
95      * <p>In this mode, the user has set a default service for this
96      *    category.
97      *
98      * <p>When using ISO-DEP card emulation with {@link HostApduService}
99      *    or {@link OffHostApduService}, if a remote NFC device selects
100      *    any of the Application IDs (AIDs)
101      *    that the default service has registered in this category,
102      *    that service will automatically be bound to to handle
103      *    the transaction.
104      */
105     public static final int SELECTION_MODE_PREFER_DEFAULT = 0;
106 
107     /**
108      * Return value for {@link #getSelectionModeForCategory(String)}.
109      *
110      * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
111      *    or {@link OffHostApduService}, whenever an Application ID (AID) of this category
112      *    is selected, the user is asked which service he wants to use to handle
113      *    the transaction, even if there is only one matching service.
114      */
115     public static final int SELECTION_MODE_ALWAYS_ASK = 1;
116 
117     /**
118      * Return value for {@link #getSelectionModeForCategory(String)}.
119      *
120      * <p>In this mode, when using ISO-DEP card emulation with {@link HostApduService}
121      *    or {@link OffHostApduService}, the user will only be asked to select a service
122      *    if the Application ID (AID) selected by the reader has been registered by multiple
123      *    services. If there is only one service that has registered for the AID,
124      *    that service will be invoked directly.
125      */
126     public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
127 
128     static boolean sIsInitialized = false;
129     static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
130     static INfcCardEmulation sService;
131 
132     final Context mContext;
133 
CardEmulation(Context context, INfcCardEmulation service)134     private CardEmulation(Context context, INfcCardEmulation service) {
135         mContext = context.getApplicationContext();
136         sService = service;
137     }
138 
139     /**
140      * Helper to get an instance of this class.
141      *
142      * @param adapter A reference to an NfcAdapter object.
143      * @return
144      */
getInstance(NfcAdapter adapter)145     public static synchronized CardEmulation getInstance(NfcAdapter adapter) {
146         if (adapter == null) throw new NullPointerException("NfcAdapter is null");
147         Context context = adapter.getContext();
148         if (context == null) {
149             Log.e(TAG, "NfcAdapter context is null.");
150             throw new UnsupportedOperationException();
151         }
152         if (!sIsInitialized) {
153             IPackageManager pm = ActivityThread.getPackageManager();
154             if (pm == null) {
155                 Log.e(TAG, "Cannot get PackageManager");
156                 throw new UnsupportedOperationException();
157             }
158             try {
159                 if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION, 0)) {
160                     Log.e(TAG, "This device does not support card emulation");
161                     throw new UnsupportedOperationException();
162                 }
163             } catch (RemoteException e) {
164                 Log.e(TAG, "PackageManager query failed.");
165                 throw new UnsupportedOperationException();
166             }
167             sIsInitialized = true;
168         }
169         CardEmulation manager = sCardEmus.get(context);
170         if (manager == null) {
171             // Get card emu service
172             INfcCardEmulation service = adapter.getCardEmulationService();
173             if (service == null) {
174                 Log.e(TAG, "This device does not implement the INfcCardEmulation interface.");
175                 throw new UnsupportedOperationException();
176             }
177             manager = new CardEmulation(context, service);
178             sCardEmus.put(context, manager);
179         }
180         return manager;
181     }
182 
183     /**
184      * Allows an application to query whether a service is currently
185      * the default service to handle a card emulation category.
186      *
187      * <p>Note that if {@link #getSelectionModeForCategory(String)}
188      * returns {@link #SELECTION_MODE_ALWAYS_ASK} or {@link #SELECTION_MODE_ASK_IF_CONFLICT},
189      * this method will always return false. That is because in these
190      * selection modes a default can't be set at the category level. For categories where
191      * the selection mode is {@link #SELECTION_MODE_ALWAYS_ASK} or
192      * {@link #SELECTION_MODE_ASK_IF_CONFLICT}, use
193      * {@link #isDefaultServiceForAid(ComponentName, String)} to determine whether a service
194      * is the default for a specific AID.
195      *
196      * @param service The ComponentName of the service
197      * @param category The category
198      * @return whether service is currently the default service for the category.
199      *
200      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
201      */
isDefaultServiceForCategory(ComponentName service, String category)202     public boolean isDefaultServiceForCategory(ComponentName service, String category) {
203         try {
204             return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service, category);
205         } catch (RemoteException e) {
206             // Try one more time
207             recoverService();
208             if (sService == null) {
209                 Log.e(TAG, "Failed to recover CardEmulationService.");
210                 return false;
211             }
212             try {
213                 return sService.isDefaultServiceForCategory(UserHandle.myUserId(), service,
214                         category);
215             } catch (RemoteException ee) {
216                 Log.e(TAG, "Failed to recover CardEmulationService.");
217                 return false;
218             }
219         }
220     }
221 
222     /**
223      *
224      * Allows an application to query whether a service is currently
225      * the default handler for a specified ISO7816-4 Application ID.
226      *
227      * @param service The ComponentName of the service
228      * @param aid The ISO7816-4 Application ID
229      * @return whether the service is the default handler for the specified AID
230      *
231      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
232      */
isDefaultServiceForAid(ComponentName service, String aid)233     public boolean isDefaultServiceForAid(ComponentName service, String aid) {
234         try {
235             return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
236         } catch (RemoteException e) {
237             // Try one more time
238             recoverService();
239             if (sService == null) {
240                 Log.e(TAG, "Failed to recover CardEmulationService.");
241                 return false;
242             }
243             try {
244                 return sService.isDefaultServiceForAid(UserHandle.myUserId(), service, aid);
245             } catch (RemoteException ee) {
246                 Log.e(TAG, "Failed to reach CardEmulationService.");
247                 return false;
248             }
249         }
250     }
251 
252     /**
253      * Returns whether the user has allowed AIDs registered in the
254      * specified category to be handled by a service that is preferred
255      * by the foreground application, instead of by a pre-configured default.
256      *
257      * Foreground applications can set such preferences using the
258      * {@link #setPreferredService(Activity, ComponentName)} method.
259      *
260      * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
261      * @return whether AIDs in the category can be handled by a service
262      *         specified by the foreground app.
263      */
categoryAllowsForegroundPreference(String category)264     public boolean categoryAllowsForegroundPreference(String category) {
265         if (CATEGORY_PAYMENT.equals(category)) {
266             boolean preferForeground = false;
267             try {
268                 preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
269                         Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
270             } catch (SettingNotFoundException e) {
271             }
272             return preferForeground;
273         } else {
274             // Allowed for all other categories
275             return true;
276         }
277     }
278 
279     /**
280      * Returns the service selection mode for the passed in category.
281      * Valid return values are:
282      * <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
283      *    service for this category, which will be preferred.
284      * <p>{@link #SELECTION_MODE_ALWAYS_ASK} the user has requested to be asked
285      *    every time what service he would like to use in this category.
286      * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
287      *    to pick a service if there is a conflict.
288      * @param category The category, for example {@link #CATEGORY_PAYMENT}
289      * @return the selection mode for the passed in category
290      */
getSelectionModeForCategory(String category)291     public int getSelectionModeForCategory(String category) {
292         if (CATEGORY_PAYMENT.equals(category)) {
293             String defaultComponent = Settings.Secure.getString(mContext.getContentResolver(),
294                     Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT);
295             if (defaultComponent != null) {
296                 return SELECTION_MODE_PREFER_DEFAULT;
297             } else {
298                 return SELECTION_MODE_ALWAYS_ASK;
299             }
300         } else {
301             return SELECTION_MODE_ASK_IF_CONFLICT;
302         }
303     }
304 
305     /**
306      * Registers a list of AIDs for a specific category for the
307      * specified service.
308      *
309      * <p>If a list of AIDs for that category was previously
310      * registered for this service (either statically
311      * through the manifest, or dynamically by using this API),
312      * that list of AIDs will be replaced with this one.
313      *
314      * <p>Note that you can only register AIDs for a service that
315      * is running under the same UID as the caller of this API. Typically
316      * this means you need to call this from the same
317      * package as the service itself, though UIDs can also
318      * be shared between packages using shared UIDs.
319      *
320      * @param service The component name of the service
321      * @param category The category of AIDs to be registered
322      * @param aids A list containing the AIDs to be registered
323      * @return whether the registration was successful.
324      */
registerAidsForService(ComponentName service, String category, List<String> aids)325     public boolean registerAidsForService(ComponentName service, String category,
326             List<String> aids) {
327         AidGroup aidGroup = new AidGroup(aids, category);
328         try {
329             return sService.registerAidGroupForService(UserHandle.myUserId(), service, aidGroup);
330         } catch (RemoteException e) {
331             // Try one more time
332             recoverService();
333             if (sService == null) {
334                 Log.e(TAG, "Failed to recover CardEmulationService.");
335                 return false;
336             }
337             try {
338                 return sService.registerAidGroupForService(UserHandle.myUserId(), service,
339                         aidGroup);
340             } catch (RemoteException ee) {
341                 Log.e(TAG, "Failed to reach CardEmulationService.");
342                 return false;
343             }
344         }
345     }
346 
347     /**
348      * Retrieves the currently registered AIDs for the specified
349      * category for a service.
350      *
351      * <p>Note that this will only return AIDs that were dynamically
352      * registered using {@link #registerAidsForService(ComponentName, String, List)}
353      * method. It will *not* return AIDs that were statically registered
354      * in the manifest.
355      *
356      * @param service The component name of the service
357      * @param category The category for which the AIDs were registered,
358      *                 e.g. {@link #CATEGORY_PAYMENT}
359      * @return The list of AIDs registered for this category, or null if it couldn't be found.
360      */
getAidsForService(ComponentName service, String category)361     public List<String> getAidsForService(ComponentName service, String category) {
362         try {
363             AidGroup group =  sService.getAidGroupForService(UserHandle.myUserId(), service,
364                     category);
365             return (group != null ? group.getAids() : null);
366         } catch (RemoteException e) {
367             recoverService();
368             if (sService == null) {
369                 Log.e(TAG, "Failed to recover CardEmulationService.");
370                 return null;
371             }
372             try {
373                 AidGroup group = sService.getAidGroupForService(UserHandle.myUserId(), service,
374                         category);
375                 return (group != null ? group.getAids() : null);
376             } catch (RemoteException ee) {
377                 Log.e(TAG, "Failed to recover CardEmulationService.");
378                 return null;
379             }
380         }
381     }
382 
383     /**
384      * Removes a previously registered list of AIDs for the specified category for the
385      * service provided.
386      *
387      * <p>Note that this will only remove AIDs that were dynamically
388      * registered using the {@link #registerAidsForService(ComponentName, String, List)}
389      * method. It will *not* remove AIDs that were statically registered in
390      * the manifest. If dynamically registered AIDs are removed using
391      * this method, and a statically registered AID group for the same category
392      * exists in the manifest, the static AID group will become active again.
393      *
394      * @param service The component name of the service
395      * @param category The category of the AIDs to be removed, e.g. {@link #CATEGORY_PAYMENT}
396      * @return whether the group was successfully removed.
397      */
removeAidsForService(ComponentName service, String category)398     public boolean removeAidsForService(ComponentName service, String category) {
399         try {
400             return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
401         } catch (RemoteException e) {
402             // Try one more time
403             recoverService();
404             if (sService == null) {
405                 Log.e(TAG, "Failed to recover CardEmulationService.");
406                 return false;
407             }
408             try {
409                 return sService.removeAidGroupForService(UserHandle.myUserId(), service, category);
410             } catch (RemoteException ee) {
411                 Log.e(TAG, "Failed to reach CardEmulationService.");
412                 return false;
413             }
414         }
415     }
416 
417     /**
418      * Allows a foreground application to specify which card emulation service
419      * should be preferred while a specific Activity is in the foreground.
420      *
421      * <p>The specified Activity must currently be in resumed state. A good
422      * paradigm is to call this method in your {@link Activity#onResume}, and to call
423      * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}.
424      *
425      * <p>This method call will fail in two specific scenarios:
426      * <ul>
427      * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT}
428      * category, but the user has indicated that foreground apps are not allowed
429      * to override the default payment service.
430      * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER}
431      * category that are also handled by the default payment service, and the
432      * user has indicated that foreground apps are not allowed to override the
433      * default payment service.
434      * </ul>
435      *
436      * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine
437      * whether foreground apps can override the default payment service.
438      *
439      * <p>Note that this preference is not persisted by the OS, and hence must be
440      * called every time the Activity is resumed.
441      *
442      * @param activity The activity which prefers this service to be invoked
443      * @param service The service to be preferred while this activity is in the foreground
444      * @return whether the registration was successful
445      */
setPreferredService(Activity activity, ComponentName service)446     public boolean setPreferredService(Activity activity, ComponentName service) {
447         // Verify the activity is in the foreground before calling into NfcService
448         if (activity == null || service == null) {
449             throw new NullPointerException("activity or service or category is null");
450         }
451         if (!activity.isResumed()) {
452             throw new IllegalArgumentException("Activity must be resumed.");
453         }
454         try {
455             return sService.setPreferredService(service);
456         } catch (RemoteException e) {
457             // Try one more time
458             recoverService();
459             if (sService == null) {
460                 Log.e(TAG, "Failed to recover CardEmulationService.");
461                 return false;
462             }
463             try {
464                 return sService.setPreferredService(service);
465             } catch (RemoteException ee) {
466                 Log.e(TAG, "Failed to reach CardEmulationService.");
467                 return false;
468             }
469         }
470     }
471 
472     /**
473      * Unsets the preferred service for the specified Activity.
474      *
475      * <p>Note that the specified Activity must still be in resumed
476      * state at the time of this call. A good place to call this method
477      * is in your {@link Activity#onPause} implementation.
478      *
479      * @param activity The activity which the service was registered for
480      * @return true when successful
481      */
unsetPreferredService(Activity activity)482     public boolean unsetPreferredService(Activity activity) {
483         if (activity == null) {
484             throw new NullPointerException("activity is null");
485         }
486         if (!activity.isResumed()) {
487             throw new IllegalArgumentException("Activity must be resumed.");
488         }
489         try {
490             return sService.unsetPreferredService();
491         } catch (RemoteException e) {
492             // Try one more time
493             recoverService();
494             if (sService == null) {
495                 Log.e(TAG, "Failed to recover CardEmulationService.");
496                 return false;
497             }
498             try {
499                 return sService.unsetPreferredService();
500             } catch (RemoteException ee) {
501                 Log.e(TAG, "Failed to reach CardEmulationService.");
502                 return false;
503             }
504         }
505     }
506 
507     /**
508      * Some devices may allow an application to register all
509      * AIDs that starts with a certain prefix, e.g.
510      * "A000000004*" to register all MasterCard AIDs.
511      *
512      * Use this method to determine whether this device
513      * supports registering AID prefixes.
514      *
515      * @return whether AID prefix registering is supported on this device.
516      */
supportsAidPrefixRegistration()517     public boolean supportsAidPrefixRegistration() {
518         try {
519             return sService.supportsAidPrefixRegistration();
520         } catch (RemoteException e) {
521             recoverService();
522             if (sService == null) {
523                 Log.e(TAG, "Failed to recover CardEmulationService.");
524                 return false;
525             }
526             try {
527                 return sService.supportsAidPrefixRegistration();
528             } catch (RemoteException ee) {
529                 Log.e(TAG, "Failed to reach CardEmulationService.");
530                 return false;
531             }
532         }
533     }
534 
535     /**
536      * @hide
537      */
setDefaultServiceForCategory(ComponentName service, String category)538     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
539         try {
540             return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service, category);
541         } catch (RemoteException e) {
542             // Try one more time
543             recoverService();
544             if (sService == null) {
545                 Log.e(TAG, "Failed to recover CardEmulationService.");
546                 return false;
547             }
548             try {
549                 return sService.setDefaultServiceForCategory(UserHandle.myUserId(), service,
550                         category);
551             } catch (RemoteException ee) {
552                 Log.e(TAG, "Failed to reach CardEmulationService.");
553                 return false;
554             }
555         }
556     }
557 
558     /**
559      * @hide
560      */
setDefaultForNextTap(ComponentName service)561     public boolean setDefaultForNextTap(ComponentName service) {
562         try {
563             return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
564         } catch (RemoteException e) {
565             // Try one more time
566             recoverService();
567             if (sService == null) {
568                 Log.e(TAG, "Failed to recover CardEmulationService.");
569                 return false;
570             }
571             try {
572                 return sService.setDefaultForNextTap(UserHandle.myUserId(), service);
573             } catch (RemoteException ee) {
574                 Log.e(TAG, "Failed to reach CardEmulationService.");
575                 return false;
576             }
577         }
578     }
579 
580     /**
581      * @hide
582      */
getServices(String category)583     public List<ApduServiceInfo> getServices(String category) {
584         try {
585             return sService.getServices(UserHandle.myUserId(), category);
586         } catch (RemoteException e) {
587             // Try one more time
588             recoverService();
589             if (sService == null) {
590                 Log.e(TAG, "Failed to recover CardEmulationService.");
591                 return null;
592             }
593             try {
594                 return sService.getServices(UserHandle.myUserId(), category);
595             } catch (RemoteException ee) {
596                 Log.e(TAG, "Failed to reach CardEmulationService.");
597                 return null;
598             }
599         }
600     }
601 
602     /**
603      * A valid AID according to ISO/IEC 7816-4:
604      * <ul>
605      * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
606      * <li>Consist of only hex characters
607      * <li>Additionally, we allow an asterisk at the end, to indicate
608      *     a prefix
609      * <li>Additinally we allow an (#) at symbol at the end, to indicate
610      *     a subset
611      * </ul>
612      *
613      * @hide
614      */
isValidAid(String aid)615     public static boolean isValidAid(String aid) {
616         if (aid == null)
617             return false;
618 
619         // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
620         if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
621             Log.e(TAG, "AID " + aid + " is not a valid AID.");
622             return false;
623         }
624 
625         // If not a prefix/subset AID, the total length must be even (even # of AID chars)
626         if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
627             Log.e(TAG, "AID " + aid + " is not a valid AID.");
628             return false;
629         }
630 
631         // Verify hex characters
632         if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?\\#?")) {
633             Log.e(TAG, "AID " + aid + " is not a valid AID.");
634             return false;
635         }
636 
637         return true;
638     }
639 
recoverService()640     void recoverService() {
641         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
642         sService = adapter.getCardEmulationService();
643     }
644 
645 }
646