NfcAdapter.java revision 52d3203ef69d4babbc4dd030a15c08c0b8d1d226
1/* 2 * Copyright (C) 2010 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 17package android.nfc; 18 19import android.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.app.Activity; 22import android.app.ActivityThread; 23import android.app.OnActivityPausedListener; 24import android.app.PendingIntent; 25import android.content.Context; 26import android.content.IntentFilter; 27import android.content.pm.IPackageManager; 28import android.content.pm.PackageManager; 29import android.nfc.technology.TagTechnology; 30import android.os.IBinder; 31import android.os.RemoteException; 32import android.os.ServiceManager; 33import android.util.Log; 34 35/** 36 * Represents the device's local NFC adapter. 37 * <p> 38 * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC 39 * adapter for this Android device. 40 */ 41public final class NfcAdapter { 42 private static final String TAG = "NFC"; 43 44 /** 45 * Intent to start an activity when a tag with NDEF payload is discovered. 46 * If the tag has and NDEF payload this intent is started before 47 * {@link #ACTION_TECHNOLOGY_DISCOVERED}. 48 * 49 * If any activities respond to this intent neither 50 * {@link #ACTION_TECHNOLOGY_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. 51 */ 52 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 53 public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; 54 55 /** 56 * Intent to started when a tag is discovered. The data URI is formated as 57 * {@code vnd.android.nfc://tag/} with the path having a directory entry for each technology 58 * in the {@link Tag#getTechnologyList()} is ascending order. 59 * 60 * This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before 61 * {@link #ACTION_TAG_DISCOVERED} 62 * 63 * If any activities respond to this intent {@link #ACTION_TAG_DISCOVERED} will not be started. 64 */ 65 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 66 public static final String ACTION_TECHNOLOGY_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; 67 68 /** 69 * Intent to start an activity when a tag is discovered. 70 */ 71 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 72 public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; 73 74 /** 75 * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED 76 * @hide 77 */ 78 public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST"; 79 80 /** 81 * Mandatory Tag extra for the ACTION_TAG intents. 82 */ 83 public static final String EXTRA_TAG = "android.nfc.extra.TAG"; 84 85 /** 86 * Optional NdefMessage[] extra for the ACTION_TAG intents. 87 */ 88 public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; 89 90 /** 91 * Optional byte[] extra for the tag identifier. 92 */ 93 public static final String EXTRA_ID = "android.nfc.extra.ID"; 94 95 /** 96 * Broadcast Action: a transaction with a secure element has been detected. 97 * <p> 98 * Always contains the extra field 99 * {@link android.nfc.NfcAdapter#EXTRA_AID} 100 * @hide 101 */ 102 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 103 public static final String ACTION_TRANSACTION_DETECTED = 104 "android.nfc.action.TRANSACTION_DETECTED"; 105 106 /** 107 * Broadcast Action: an RF field ON has been detected. 108 * @hide 109 */ 110 public static final String ACTION_RF_FIELD_ON_DETECTED = 111 "android.nfc.action.RF_FIELD_ON_DETECTED"; 112 113 /** 114 * Broadcast Action: an RF Field OFF has been detected. 115 * @hide 116 */ 117 public static final String ACTION_RF_FIELD_OFF_DETECTED = 118 "android.nfc.action.RF_FIELD_OFF_DETECTED"; 119 120 /** 121 * Broadcast Action: an adapter's state changed between enabled and disabled. 122 * 123 * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains 124 * whether it's enabled or disabled, not including any information about whether it's 125 * actively enabling or disabling. 126 * 127 * @hide 128 */ 129 public static final String ACTION_ADAPTER_STATE_CHANGE = 130 "android.nfc.action.ADAPTER_STATE_CHANGE"; 131 132 /** 133 * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is. 134 * 135 * @hide 136 */ 137 public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled"; 138 139 /** 140 * Mandatory byte array extra field in 141 * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}. 142 * <p> 143 * Contains the AID of the applet involved in the transaction. 144 * @hide 145 */ 146 public static final String EXTRA_AID = "android.nfc.extra.AID"; 147 148 /** 149 * LLCP link status: The LLCP link is activated. 150 * @hide 151 */ 152 public static final int LLCP_LINK_STATE_ACTIVATED = 0; 153 154 /** 155 * LLCP link status: The LLCP link is deactivated. 156 * @hide 157 */ 158 public static final int LLCP_LINK_STATE_DEACTIVATED = 1; 159 160 /** 161 * Broadcast Action: the LLCP link state changed. 162 * <p> 163 * Always contains the extra field 164 * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}. 165 * @hide 166 */ 167 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 168 public static final String ACTION_LLCP_LINK_STATE_CHANGED = 169 "android.nfc.action.LLCP_LINK_STATE_CHANGED"; 170 171 /** 172 * Used as int extra field in 173 * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}. 174 * <p> 175 * It contains the new state of the LLCP link. 176 * @hide 177 */ 178 public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE"; 179 180 /** 181 * Tag Reader Discovery mode 182 * @hide 183 */ 184 private static final int DISCOVERY_MODE_TAG_READER = 0; 185 186 /** 187 * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an 188 * NFC-IP1 communication. Implementations should not assume that the 189 * controller will end up behaving as an NFC-IP1 target or initiator and 190 * should handle both cases, depending on the type of the remote peer type. 191 * @hide 192 */ 193 private static final int DISCOVERY_MODE_NFCIP1 = 1; 194 195 /** 196 * Card Emulation mode Enables the manager to act as an NFC tag. Provided 197 * that a Secure Element (an UICC for instance) is connected to the NFC 198 * controller through its SWP interface, it can be exposed to the outside 199 * NFC world and be addressed by external readers the same way they would 200 * with a tag. 201 * <p> 202 * Which Secure Element is exposed is implementation-dependent. 203 * 204 * @hide 205 */ 206 private static final int DISCOVERY_MODE_CARD_EMULATION = 2; 207 208 209 // Guarded by NfcAdapter.class 210 private static boolean sIsInitialized = false; 211 212 // Final after first constructor, except for 213 // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort 214 // recovery 215 private static INfcAdapter sService; 216 private static INfcTag sTagService; 217 218 /** 219 * Helper to check if this device has FEATURE_NFC, but without using 220 * a context. 221 * Equivalent to 222 * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) 223 */ 224 private static boolean hasNfcFeature() { 225 IPackageManager pm = ActivityThread.getPackageManager(); 226 if (pm == null) { 227 Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); 228 return false; 229 } 230 try { 231 return pm.hasSystemFeature(PackageManager.FEATURE_NFC); 232 } catch (RemoteException e) { 233 Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); 234 return false; 235 } 236 } 237 238 private static synchronized INfcAdapter setupService() { 239 if (!sIsInitialized) { 240 sIsInitialized = true; 241 242 /* is this device meant to have NFC */ 243 if (!hasNfcFeature()) { 244 Log.v(TAG, "this device does not have NFC support"); 245 return null; 246 } 247 248 sService = getServiceInterface(); 249 if (sService == null) { 250 Log.e(TAG, "could not retrieve NFC service"); 251 return null; 252 } 253 try { 254 sTagService = sService.getNfcTagInterface(); 255 } catch (RemoteException e) { 256 Log.e(TAG, "could not retrieve NFC Tag service"); 257 return null; 258 } 259 } 260 return sService; 261 } 262 263 /** get handle to NFC service interface */ 264 private static INfcAdapter getServiceInterface() { 265 /* get a handle to NFC service */ 266 IBinder b = ServiceManager.getService("nfc"); 267 if (b == null) { 268 return null; 269 } 270 return INfcAdapter.Stub.asInterface(b); 271 } 272 273 /** 274 * Helper to get the default NFC Adapter. 275 * <p> 276 * Most Android devices will only have one NFC Adapter (NFC Controller). 277 * <p> 278 * This helper is the equivalent of: 279 * <pre>{@code 280 * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 281 * NfcAdapter adapter = manager.getDefaultAdapter(); 282 * }</pre> 283 * @param context the calling application's context 284 * 285 * @return the default NFC adapter, or null if no NFC adapter exists 286 */ 287 public static NfcAdapter getDefaultAdapter(Context context) { 288 /* use getSystemService() instead of just instantiating to take 289 * advantage of the context's cached NfcManager & NfcAdapter */ 290 NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 291 return manager.getDefaultAdapter(); 292 } 293 294 /** 295 * Get a handle to the default NFC Adapter on this Android device. 296 * <p> 297 * Most Android devices will only have one NFC Adapter (NFC Controller). 298 * 299 * @return the default NFC adapter, or null if no NFC adapter exists 300 * @deprecated use {@link #getDefaultAdapter(Context)} 301 */ 302 @Deprecated 303 public static NfcAdapter getDefaultAdapter() { 304 Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + 305 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); 306 return new NfcAdapter(null); 307 } 308 309 /*package*/ NfcAdapter(Context context) { 310 if (setupService() == null) { 311 throw new UnsupportedOperationException(); 312 } 313 } 314 315 /** 316 * Returns the binder interface to the service. 317 * @hide 318 */ 319 public INfcAdapter getService() { 320 return sService; 321 } 322 323 /** 324 * Returns the binder interface to the tag service. 325 * @hide 326 */ 327 public INfcTag getTagService() { 328 return sTagService; 329 } 330 331 /** 332 * NFC service dead - attempt best effort recovery 333 * @hide 334 */ 335 public void attemptDeadServiceRecovery(Exception e) { 336 Log.e(TAG, "NFC service dead - attempting to recover", e); 337 INfcAdapter service = getServiceInterface(); 338 if (service == null) { 339 Log.e(TAG, "could not retrieve NFC service during service recovery"); 340 // nothing more can be done now, sService is still stale, we'll hit 341 // this recovery path again later 342 return; 343 } 344 // assigning to sService is not thread-safe, but this is best-effort code 345 // and on a well-behaved system should never happen 346 sService = service; 347 try { 348 sTagService = service.getNfcTagInterface(); 349 } catch (RemoteException ee) { 350 Log.e(TAG, "could not retrieve NFC tag service during service recovery"); 351 // nothing more can be done now, sService is still stale, we'll hit 352 // this recovery path again later 353 } 354 355 return; 356 } 357 358 /** 359 * Return true if this NFC Adapter has any features enabled. 360 * <p> 361 * If this method returns false, then applications should request the user 362 * turn on NFC tag discovery in Settings. 363 * <p> 364 * If this method returns false, the NFC hardware is guaranteed not to 365 * perform or respond to any NFC communication. 366 * 367 * @return true if this NFC Adapter is enabled to discover new tags 368 */ 369 public boolean isEnabled() { 370 try { 371 return sService.isEnabled(); 372 } catch (RemoteException e) { 373 attemptDeadServiceRecovery(e); 374 return false; 375 } 376 } 377 378 /** 379 * Enable NFC hardware. 380 * <p> 381 * NOTE: may block for ~second or more. Poor API. Avoid 382 * calling from the UI thread. 383 * 384 * @hide 385 */ 386 public boolean enable() { 387 try { 388 return sService.enable(); 389 } catch (RemoteException e) { 390 attemptDeadServiceRecovery(e); 391 return false; 392 } 393 } 394 395 /** 396 * Disable NFC hardware. 397 * No NFC features will work after this call, and the hardware 398 * will not perform or respond to any NFC communication. 399 * <p> 400 * NOTE: may block for ~second or more. Poor API. Avoid 401 * calling from the UI thread. 402 * 403 * @hide 404 */ 405 public boolean disable() { 406 try { 407 return sService.disable(); 408 } catch (RemoteException e) { 409 attemptDeadServiceRecovery(e); 410 return false; 411 } 412 } 413 414 class ForegroundDispatchPausedListener implements OnActivityPausedListener { 415 @Override 416 public void onPaused(Activity activity) { 417 disableForegroundDispatchInternal(activity, true); 418 } 419 } 420 421 /** 422 * Enables foreground dispatching to the given Activity. This will force all NFC Intents that 423 * match the given filters to be delivered to the activity bypassing the standard dispatch 424 * mechanism. 425 * 426 * This method must be called from the main thread. 427 * 428 * @param activity the Activity to dispatch to 429 * @param intent the PendingIntent to start for the dispatch 430 * @param filters the IntentFilters to override dispatching for 431 * @throws IllegalStateException 432 */ 433 public void enableForegroundDispatch(Activity activity, PendingIntent intent, 434 IntentFilter... filters) { 435 if (activity == null || intent == null || filters == null) { 436 throw new NullPointerException(); 437 } 438 if (!activity.isResumed()) { 439 throw new IllegalStateException("Foregorund dispatching can onlly be enabled " + 440 "when your activity is resumed"); 441 } 442 try { 443 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 444 new ForegroundDispatchPausedListener()); 445 sService.enableForegroundDispatch(activity.getComponentName(), intent, filters); 446 } catch (RemoteException e) { 447 attemptDeadServiceRecovery(e); 448 } 449 } 450 451 /** 452 * Disables foreground activity dispatching setup with 453 * {@link #enableForegroundDispatch}. This must be called before the Activity returns from 454 * it's <code>onPause()</code> or this method will throw an IllegalStateException. 455 * 456 * This method must be called from the main thread. 457 */ 458 public void disableForegroundDispatch(Activity activity) { 459 disableForegroundDispatchInternal(activity, false); 460 } 461 462 void disableForegroundDispatchInternal(Activity activity, boolean force) { 463 try { 464 sService.disableForegroundDispatch(activity.getComponentName()); 465 if (!force && !activity.isResumed()) { 466 throw new IllegalStateException("You must disable forgeground dispatching " + 467 "while your activity is still resumed"); 468 } 469 } catch (RemoteException e) { 470 attemptDeadServiceRecovery(e); 471 } 472 } 473 474 /** 475 * Retrieve a TagTechnology object used to interact with a Tag that is 476 * in field. 477 * <p> 478 * @return TagTechnology object, or null if not present 479 */ 480 public TagTechnology getTechnology(Tag tag, int tech) { 481 return tag.getTechnology(NfcAdapter.this, tech); 482 } 483 484 /** 485 * Set the NDEF Message that this NFC adapter should appear as to Tag 486 * readers. 487 * <p> 488 * Any Tag reader can read the contents of the local tag when it is in 489 * proximity, without any further user confirmation. 490 * <p> 491 * The implementation of this method must either 492 * <ul> 493 * <li>act as a passive tag containing this NDEF message 494 * <li>provide the NDEF message on over LLCP to peer NFC adapters 495 * </ul> 496 * The NDEF message is preserved across reboot. 497 * <p>Requires {@link android.Manifest.permission#NFC} permission. 498 * 499 * @param message NDEF message to make public 500 * @hide 501 */ 502 public void setLocalNdefMessage(NdefMessage message) { 503 try { 504 sService.localSet(message); 505 } catch (RemoteException e) { 506 attemptDeadServiceRecovery(e); 507 } 508 } 509 510 /** 511 * Get the NDEF Message that this adapter appears as to Tag readers. 512 * <p>Requires {@link android.Manifest.permission#NFC} permission. 513 * 514 * @return NDEF Message that is publicly readable 515 * @hide 516 */ 517 public NdefMessage getLocalNdefMessage() { 518 try { 519 return sService.localGet(); 520 } catch (RemoteException e) { 521 attemptDeadServiceRecovery(e); 522 return null; 523 } 524 } 525 526 /** 527 * Create an Nfc Secure Element Connection 528 * @hide 529 */ 530 public NfcSecureElement createNfcSecureElementConnection() { 531 try { 532 return new NfcSecureElement(sService.getNfcSecureElementInterface()); 533 } catch (RemoteException e) { 534 Log.e(TAG, "createNfcSecureElementConnection failed", e); 535 return null; 536 } 537 } 538} 539