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