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