1 /* 2 * Copyright (C) 2010 The Android Open Source Project Licensed under the Apache 3 * License, Version 2.0 (the "License"); you may not use this file except in 4 * compliance with the License. You may obtain a copy of the License at 5 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law 6 * or agreed to in writing, software distributed under the License is 7 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 * KIND, either express or implied. See the License for the specific language 9 * governing permissions and limitations under the License. 10 */ 11 12 package android.nfc; 13 14 import java.lang.UnsupportedOperationException; 15 16 import android.annotation.SdkConstant; 17 import android.annotation.SdkConstant.SdkConstantType; 18 import android.app.ActivityThread; 19 import android.content.Context; 20 import android.content.pm.IPackageManager; 21 import android.content.pm.PackageManager; 22 import android.nfc.INfcAdapter; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.os.ServiceManager; 26 import android.util.Log; 27 28 /** 29 * Represents the device's local NFC adapter. 30 * <p> 31 * Use the static {@link #getDefaultAdapter} method to get the default NFC 32 * Adapter for this Android device. Most Android devices will have only one NFC 33 * Adapter, and {@link #getDefaultAdapter} returns the singleton object. 34 */ 35 public final class NfcAdapter { 36 /** 37 * Intent to start an activity when a tag is discovered. 38 */ 39 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 40 public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; 41 42 /** 43 * Mandatory Tag extra for the ACTION_TAG intents. 44 * @hide 45 */ 46 public static final String EXTRA_TAG = "android.nfc.extra.TAG"; 47 48 /** 49 * Optional NdefMessage[] extra for the ACTION_TAG intents. 50 */ 51 public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; 52 53 /** 54 * Optional byte[] extra for the tag identifier. 55 */ 56 public static final String EXTRA_ID = "android.nfc.extra.ID"; 57 58 /** 59 * Broadcast Action: a transaction with a secure element has been detected. 60 * <p> 61 * Always contains the extra field 62 * {@link android.nfc.NfcAdapter#EXTRA_AID} 63 * @hide 64 */ 65 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 66 public static final String ACTION_TRANSACTION_DETECTED = 67 "android.nfc.action.TRANSACTION_DETECTED"; 68 69 /** 70 * Broadcast Action: an adapter's state changed between enabled and disabled. 71 * 72 * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains 73 * whether it's enabled or disabled, not including any information about whether it's 74 * actively enabling or disabling. 75 * 76 * @hide 77 */ 78 public static final String ACTION_ADAPTER_STATE_CHANGE = 79 "android.nfc.action.ADAPTER_STATE_CHANGE"; 80 81 /** 82 * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is. 83 * 84 * @hide 85 */ 86 public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled"; 87 88 /** 89 * Mandatory byte array extra field in 90 * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}. 91 * <p> 92 * Contains the AID of the applet involved in the transaction. 93 * @hide 94 */ 95 public static final String EXTRA_AID = "android.nfc.extra.AID"; 96 97 /** 98 * LLCP link status: The LLCP link is activated. 99 * @hide 100 */ 101 public static final int LLCP_LINK_STATE_ACTIVATED = 0; 102 103 /** 104 * LLCP link status: The LLCP link is deactivated. 105 * @hide 106 */ 107 public static final int LLCP_LINK_STATE_DEACTIVATED = 1; 108 109 /** 110 * Broadcast Action: the LLCP link state changed. 111 * <p> 112 * Always contains the extra field 113 * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}. 114 * @hide 115 */ 116 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 117 public static final String ACTION_LLCP_LINK_STATE_CHANGED = 118 "android.nfc.action.LLCP_LINK_STATE_CHANGED"; 119 120 /** 121 * Used as int extra field in 122 * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}. 123 * <p> 124 * It contains the new state of the LLCP link. 125 * @hide 126 */ 127 public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE"; 128 129 /** 130 * Tag Reader Discovery mode 131 * @hide 132 */ 133 private static final int DISCOVERY_MODE_TAG_READER = 0; 134 135 /** 136 * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an 137 * NFC-IP1 communication. Implementations should not assume that the 138 * controller will end up behaving as an NFC-IP1 target or initiator and 139 * should handle both cases, depending on the type of the remote peer type. 140 * @hide 141 */ 142 private static final int DISCOVERY_MODE_NFCIP1 = 1; 143 144 /** 145 * Card Emulation mode Enables the manager to act as an NFC tag. Provided 146 * that a Secure Element (an UICC for instance) is connected to the NFC 147 * controller through its SWP interface, it can be exposed to the outside 148 * NFC world and be addressed by external readers the same way they would 149 * with a tag. 150 * <p> 151 * Which Secure Element is exposed is implementation-dependent. 152 * 153 * @hide 154 */ 155 private static final int DISCOVERY_MODE_CARD_EMULATION = 2; 156 157 private static final String TAG = "NFC"; 158 159 // Both guarded by NfcAdapter.class: 160 private static boolean sIsInitialized = false; 161 private static NfcAdapter sAdapter; 162 163 // Final after construction, except for attemptDeadServiceRecovery() 164 // when NFC crashes. 165 // Not locked - we accept a best effort attempt when NFC crashes. 166 /*package*/ INfcAdapter mService; 167 168 private NfcAdapter(INfcAdapter service) { 169 mService = service; 170 } 171 172 /** 173 * Helper to check if this device has FEATURE_NFC, but without using 174 * a context. 175 * Equivalent to 176 * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) 177 */ 178 private static boolean hasNfcFeature() { 179 IPackageManager pm = ActivityThread.getPackageManager(); 180 if (pm == null) { 181 Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); 182 return false; 183 } 184 try { 185 return pm.hasSystemFeature(PackageManager.FEATURE_NFC); 186 } catch (RemoteException e) { 187 Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); 188 return false; 189 } 190 } 191 192 /** get handle to NFC service interface */ 193 private static synchronized INfcAdapter getServiceInterface() { 194 /* get a handle to NFC service */ 195 IBinder b = ServiceManager.getService("nfc"); 196 if (b == null) { 197 return null; 198 } 199 return INfcAdapter.Stub.asInterface(b); 200 } 201 202 /** 203 * Get a handle to the default NFC Adapter on this Android device. 204 * <p> 205 * Most Android devices will only have one NFC Adapter (NFC Controller). 206 * 207 * @return the default NFC adapter, or null if no NFC adapter exists 208 */ 209 public static NfcAdapter getDefaultAdapter() { 210 synchronized (NfcAdapter.class) { 211 if (sIsInitialized) { 212 return sAdapter; 213 } 214 sIsInitialized = true; 215 216 /* is this device meant to have NFC */ 217 if (!hasNfcFeature()) { 218 Log.v(TAG, "this device does not have NFC support"); 219 return null; 220 } 221 222 INfcAdapter service = getServiceInterface(); 223 if (service == null) { 224 Log.e(TAG, "could not retrieve NFC service"); 225 return null; 226 } 227 228 sAdapter = new NfcAdapter(service); 229 return sAdapter; 230 } 231 } 232 233 /** NFC service dead - attempt best effort recovery */ 234 /*package*/ void attemptDeadServiceRecovery(Exception e) { 235 Log.e(TAG, "NFC service dead - attempting to recover", e); 236 INfcAdapter service = getServiceInterface(); 237 if (service == null) { 238 Log.e(TAG, "could not retrieve NFC service during service recovery"); 239 return; 240 } 241 /* assigning to mService is not thread-safe, but this is best-effort code 242 * and on a well-behaved system should never happen */ 243 mService = service; 244 return; 245 } 246 247 /** 248 * Return true if this NFC Adapter has any features enabled. 249 * <p> 250 * If this method returns false, then applications should request the user 251 * turn on NFC tag discovery in Settings. 252 * <p> 253 * If this method returns false, the NFC hardware is guaranteed not to 254 * perform or respond to any NFC communication. 255 * 256 * @return true if this NFC Adapter is enabled to discover new tags 257 */ 258 public boolean isEnabled() { 259 try { 260 return mService.isEnabled(); 261 } catch (RemoteException e) { 262 attemptDeadServiceRecovery(e); 263 return false; 264 } 265 } 266 267 /** 268 * Enable NFC hardware. 269 * <p> 270 * NOTE: may block for ~second or more. Poor API. Avoid 271 * calling from the UI thread. 272 * 273 * @hide 274 */ 275 public boolean enable() { 276 try { 277 return mService.enable(); 278 } catch (RemoteException e) { 279 attemptDeadServiceRecovery(e); 280 return false; 281 } 282 } 283 284 /** 285 * Disable NFC hardware. 286 * No NFC features will work after this call, and the hardware 287 * will not perform or respond to any NFC communication. 288 * <p> 289 * NOTE: may block for ~second or more. Poor API. Avoid 290 * calling from the UI thread. 291 * 292 * @hide 293 */ 294 public boolean disable() { 295 try { 296 return mService.disable(); 297 } catch (RemoteException e) { 298 attemptDeadServiceRecovery(e); 299 return false; 300 } 301 } 302 303 /** 304 * Create a raw tag connection to the default Target 305 * <p>Requires {@link android.Manifest.permission#NFC} permission. 306 * @hide 307 */ 308 public RawTagConnection createRawTagConnection(Tag tag) { 309 if (tag.mServiceHandle == 0) { 310 throw new IllegalArgumentException("mock tag cannot be used for connections"); 311 } 312 try { 313 return new RawTagConnection(this, tag); 314 } catch (RemoteException e) { 315 attemptDeadServiceRecovery(e); 316 return null; 317 } 318 } 319 320 /** 321 * Create a raw tag connection to the specified Target 322 * <p>Requires {@link android.Manifest.permission#NFC} permission. 323 * @hide 324 */ 325 public RawTagConnection createRawTagConnection(Tag tag, String target) { 326 if (tag.mServiceHandle == 0) { 327 throw new IllegalArgumentException("mock tag cannot be used for connections"); 328 } 329 try { 330 return new RawTagConnection(this, tag, target); 331 } catch (RemoteException e) { 332 attemptDeadServiceRecovery(e); 333 return null; 334 } 335 } 336 337 /** 338 * Create an NDEF tag connection to the default Target 339 * <p>Requires {@link android.Manifest.permission#NFC} permission. 340 * @hide 341 */ 342 public NdefTagConnection createNdefTagConnection(NdefTag tag) { 343 if (tag.mServiceHandle == 0) { 344 throw new IllegalArgumentException("mock tag cannot be used for connections"); 345 } 346 try { 347 return new NdefTagConnection(this, tag); 348 } catch (RemoteException e) { 349 attemptDeadServiceRecovery(e); 350 return null; 351 } 352 } 353 354 /** 355 * Create an NDEF tag connection to the specified Target 356 * <p>Requires {@link android.Manifest.permission#NFC} permission. 357 * @hide 358 */ 359 public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) { 360 if (tag.mServiceHandle == 0) { 361 throw new IllegalArgumentException("mock tag cannot be used for connections"); 362 } 363 try { 364 return new NdefTagConnection(this, tag, target); 365 } catch (RemoteException e) { 366 attemptDeadServiceRecovery(e); 367 return null; 368 } 369 } 370 } 371