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