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