NfcAdapter.java revision 253c509cb0d36b39c3d878c0caf7f1eca194068a
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 isEnabled(); // NOP call to recover sService if it is stale 320 return sService; 321 } 322 323 /** 324 * Returns the binder interface to the tag service. 325 * @hide 326 */ 327 public INfcTag getTagService() { 328 isEnabled(); // NOP call to recover sTagService if it is stale 329 return sTagService; 330 } 331 332 /** 333 * NFC service dead - attempt best effort recovery 334 * @hide 335 */ 336 public void attemptDeadServiceRecovery(Exception e) { 337 Log.e(TAG, "NFC service dead - attempting to recover", e); 338 INfcAdapter service = getServiceInterface(); 339 if (service == null) { 340 Log.e(TAG, "could not retrieve NFC service during service recovery"); 341 // nothing more can be done now, sService is still stale, we'll hit 342 // this recovery path again later 343 return; 344 } 345 // assigning to sService is not thread-safe, but this is best-effort code 346 // and on a well-behaved system should never happen 347 sService = service; 348 try { 349 sTagService = service.getNfcTagInterface(); 350 } catch (RemoteException ee) { 351 Log.e(TAG, "could not retrieve NFC tag service during service recovery"); 352 // nothing more can be done now, sService is still stale, we'll hit 353 // this recovery path again later 354 } 355 356 return; 357 } 358 359 /** 360 * Return true if this NFC Adapter has any features enabled. 361 * <p> 362 * If this method returns false, then applications should request the user 363 * turn on NFC tag discovery in Settings. 364 * <p> 365 * If this method returns false, the NFC hardware is guaranteed not to 366 * perform or respond to any NFC communication. 367 * 368 * @return true if this NFC Adapter is enabled to discover new tags 369 */ 370 public boolean isEnabled() { 371 try { 372 return sService.isEnabled(); 373 } catch (RemoteException e) { 374 attemptDeadServiceRecovery(e); 375 return false; 376 } 377 } 378 379 /** 380 * Enable NFC hardware. 381 * <p> 382 * NOTE: may block for ~second or more. Poor API. Avoid 383 * calling from the UI thread. 384 * 385 * @hide 386 */ 387 public boolean enable() { 388 try { 389 return sService.enable(); 390 } catch (RemoteException e) { 391 attemptDeadServiceRecovery(e); 392 return false; 393 } 394 } 395 396 /** 397 * Disable NFC hardware. 398 * No NFC features will work after this call, and the hardware 399 * will not perform or respond to any NFC communication. 400 * <p> 401 * NOTE: may block for ~second or more. Poor API. Avoid 402 * calling from the UI thread. 403 * 404 * @hide 405 */ 406 public boolean disable() { 407 try { 408 return sService.disable(); 409 } catch (RemoteException e) { 410 attemptDeadServiceRecovery(e); 411 return false; 412 } 413 } 414 415 /** 416 * Retrieve a TagTechnology object used to interact with a Tag that is 417 * in field. 418 * <p> 419 * @return TagTechnology object, or null if not present 420 */ 421 public TagTechnology getTechnology(Tag tag, int tech) { 422 return tag.getTechnology(NfcAdapter.this, tech); 423 } 424 425 /** 426 * Set the NDEF Message that this NFC adapter should appear as to Tag 427 * readers. 428 * <p> 429 * Any Tag reader can read the contents of the local tag when it is in 430 * proximity, without any further user confirmation. 431 * <p> 432 * The implementation of this method must either 433 * <ul> 434 * <li>act as a passive tag containing this NDEF message 435 * <li>provide the NDEF message on over LLCP to peer NFC adapters 436 * </ul> 437 * The NDEF message is preserved across reboot. 438 * <p>Requires {@link android.Manifest.permission#NFC} permission. 439 * 440 * @param message NDEF message to make public 441 * @hide 442 */ 443 public void setLocalNdefMessage(NdefMessage message) { 444 try { 445 sService.localSet(message); 446 } catch (RemoteException e) { 447 attemptDeadServiceRecovery(e); 448 } 449 } 450 451 /** 452 * Get the NDEF Message that this adapter appears as to Tag readers. 453 * <p>Requires {@link android.Manifest.permission#NFC} permission. 454 * 455 * @return NDEF Message that is publicly readable 456 * @hide 457 */ 458 public NdefMessage getLocalNdefMessage() { 459 try { 460 return sService.localGet(); 461 } catch (RemoteException e) { 462 attemptDeadServiceRecovery(e); 463 return null; 464 } 465 } 466 467 /** 468 * Create an Nfc Secure Element Connection 469 * @hide 470 */ 471 public NfcSecureElement createNfcSecureElementConnection() { 472 try { 473 return new NfcSecureElement(sService.getNfcSecureElementInterface()); 474 } catch (RemoteException e) { 475 Log.e(TAG, "createNfcSecureElementConnection failed", e); 476 return null; 477 } 478 } 479} 480