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