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