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