NfcAdapter.java revision 3bba8d0457408421a6468f03bbb36e9ff32b81cf
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 java.util.HashMap; 20 21import android.annotation.SdkConstant; 22import android.annotation.SdkConstant.SdkConstantType; 23import android.app.Activity; 24import android.app.ActivityThread; 25import android.app.OnActivityPausedListener; 26import android.app.PendingIntent; 27import android.content.Context; 28import android.content.IntentFilter; 29import android.content.pm.IPackageManager; 30import android.content.pm.PackageManager; 31import android.net.Uri; 32import android.nfc.tech.MifareClassic; 33import android.nfc.tech.Ndef; 34import android.nfc.tech.NfcA; 35import android.nfc.tech.NfcF; 36import android.os.IBinder; 37import android.os.RemoteException; 38import android.os.ServiceManager; 39import android.util.Log; 40 41/** 42 * Represents the local NFC adapter. 43 * <p> 44 * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC 45 * adapter for this Android device. 46 * 47 * <div class="special reference"> 48 * <h3>Developer Guides</h3> 49 * <p>For more information about using NFC, read the 50 * <a href="{@docRoot}guide/topics/nfc/index.html">Near Field Communication</a> developer guide.</p> 51 * </div> 52 */ 53public final class NfcAdapter { 54 static final String TAG = "NFC"; 55 56 /** 57 * Intent to start an activity when a tag with NDEF payload is discovered. 58 * 59 * <p>The system inspects the first {@link NdefRecord} in the first {@link NdefMessage} and 60 * looks for a URI, SmartPoster, or MIME record. If a URI or SmartPoster record is found the 61 * intent will contain the URI in its data field. If a MIME record is found the intent will 62 * contain the MIME type in its type field. This allows activities to register 63 * {@link IntentFilter}s targeting specific content on tags. Activities should register the 64 * most specific intent filters possible to avoid the activity chooser dialog, which can 65 * disrupt the interaction with the tag as the user interacts with the screen. 66 * 67 * <p>If the tag has an NDEF payload this intent is started before 68 * {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither 69 * {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. 70 * 71 * <p>The MIME type or data URI of this intent are normalized before dispatch - 72 * so that MIME, URI scheme and URI host are always lower-case. 73 */ 74 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 75 public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; 76 77 /** 78 * Intent to start an activity when a tag is discovered and activities are registered for the 79 * specific technologies on the tag. 80 * 81 * <p>To receive this intent an activity must include an intent filter 82 * for this action and specify the desired tech types in a 83 * manifest <code>meta-data</code> entry. Here is an example manfiest entry: 84 * <pre> 85 * <activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"> 86 * <!-- Add a technology filter --> 87 * <intent-filter> 88 * <action android:name="android.nfc.action.TECH_DISCOVERED" /> 89 * </intent-filter> 90 * 91 * <meta-data android:name="android.nfc.action.TECH_DISCOVERED" 92 * android:resource="@xml/filter_nfc" 93 * /> 94 * </activity> 95 * </pre> 96 * 97 * <p>The meta-data XML file should contain one or more <code>tech-list</code> entries 98 * each consisting or one or more <code>tech</code> entries. The <code>tech</code> entries refer 99 * to the qualified class name implementing the technology, for example "android.nfc.tech.NfcA". 100 * 101 * <p>A tag matches if any of the 102 * <code>tech-list</code> sets is a subset of {@link Tag#getTechList() Tag.getTechList()}. Each 103 * of the <code>tech-list</code>s is considered independently and the 104 * activity is considered a match is any single <code>tech-list</code> matches the tag that was 105 * discovered. This provides AND and OR semantics for filtering desired techs. Here is an 106 * example that will match any tag using {@link NfcF} or any tag using {@link NfcA}, 107 * {@link MifareClassic}, and {@link Ndef}: 108 * 109 * <pre> 110 * <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 111 * <!-- capture anything using NfcF --> 112 * <tech-list> 113 * <tech>android.nfc.tech.NfcF</tech> 114 * </tech-list> 115 * 116 * <!-- OR --> 117 * 118 * <!-- capture all MIFARE Classics with NDEF payloads --> 119 * <tech-list> 120 * <tech>android.nfc.tech.NfcA</tech> 121 * <tech>android.nfc.tech.MifareClassic</tech> 122 * <tech>android.nfc.tech.Ndef</tech> 123 * </tech-list> 124 * </resources> 125 * </pre> 126 * 127 * <p>This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before 128 * {@link #ACTION_TAG_DISCOVERED}. If any activities respond to {@link #ACTION_NDEF_DISCOVERED} 129 * this intent will not be started. If any activities respond to this intent 130 * {@link #ACTION_TAG_DISCOVERED} will not be started. 131 */ 132 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 133 public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; 134 135 /** 136 * Intent to start an activity when a tag is discovered. 137 * 138 * <p>This intent will not be started when a tag is discovered if any activities respond to 139 * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. 140 */ 141 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 142 public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; 143 144 /** 145 * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED 146 * @hide 147 */ 148 public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST"; 149 150 /** 151 * Mandatory extra containing the {@link Tag} that was discovered for the 152 * {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and 153 * {@link #ACTION_TAG_DISCOVERED} intents. 154 */ 155 public static final String EXTRA_TAG = "android.nfc.extra.TAG"; 156 157 /** 158 * Extra containing an array of {@link NdefMessage} present on the discovered tag.<p> 159 * This extra is mandatory for {@link #ACTION_NDEF_DISCOVERED} intents, 160 * and optional for {@link #ACTION_TECH_DISCOVERED}, and 161 * {@link #ACTION_TAG_DISCOVERED} intents.<p> 162 * When this extra is present there will always be at least one 163 * {@link NdefMessage} element. Most NDEF tags have only one NDEF message, 164 * but we use an array for future compatibility. 165 */ 166 public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; 167 168 /** 169 * Optional extra containing a byte array containing the ID of the discovered tag for 170 * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and 171 * {@link #ACTION_TAG_DISCOVERED} intents. 172 */ 173 public static final String EXTRA_ID = "android.nfc.extra.ID"; 174 175 /** 176 * Broadcast Action: The state of the local NFC adapter has been 177 * changed. 178 * <p>For example, NFC has been turned on or off. 179 * <p>Always contains the extra field {@link #EXTRA_STATE} 180 * @hide 181 */ 182 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 183 public static final String ACTION_ADAPTER_STATE_CHANGED = 184 "android.nfc.action.ADAPTER_STATE_CHANGED"; 185 186 /** 187 * Used as an int extra field in {@link #ACTION_STATE_CHANGED} 188 * intents to request the current power state. Possible values are: 189 * {@link #STATE_OFF}, 190 * {@link #STATE_TURNING_ON}, 191 * {@link #STATE_ON}, 192 * {@link #STATE_TURNING_OFF}, 193 * @hide 194 */ 195 public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; 196 197 /** @hide */ 198 public static final int STATE_OFF = 1; 199 /** @hide */ 200 public static final int STATE_TURNING_ON = 2; 201 /** @hide */ 202 public static final int STATE_ON = 3; 203 /** @hide */ 204 public static final int STATE_TURNING_OFF = 4; 205 206 /** @hide */ 207 public static final String ACTION_HANDOVER_TRANSFER_STARTED = 208 "android.nfc.action.HANDOVER_TRANSFER_STARTED"; 209 210 /** @hide */ 211 public static final String ACTION_HANDOVER_TRANSFER_DONE = 212 "android.nfc.action.HANDOVER_TRANSFER_DONE"; 213 214 /** @hide */ 215 public static final String EXTRA_HANDOVER_TRANSFER_STATUS = 216 "android.nfc.extra.HANDOVER_TRANSFER_STATUS"; 217 218 /** @hide */ 219 public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0; 220 /** @hide */ 221 public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1; 222 223 /** @hide */ 224 public static final String EXTRA_HANDOVER_TRANSFER_URI = 225 "android.nfc.extra.HANDOVER_TRANSFER_URI"; 226 227 // Guarded by NfcAdapter.class 228 static boolean sIsInitialized = false; 229 230 // Final after first constructor, except for 231 // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort 232 // recovery 233 static INfcAdapter sService; 234 static INfcTag sTagService; 235 236 /** 237 * The NfcAdapter object for each application context. 238 * There is a 1-1 relationship between application context and 239 * NfcAdapter object. 240 */ 241 static HashMap<Context, NfcAdapter> sNfcAdapters = new HashMap(); //guard by NfcAdapter.class 242 243 /** 244 * NfcAdapter used with a null context. This ctor was deprecated but we have 245 * to support it for backwards compatibility. New methods that require context 246 * might throw when called on the null-context NfcAdapter. 247 */ 248 static NfcAdapter sNullContextNfcAdapter; // protected by NfcAdapter.class 249 250 final NfcActivityManager mNfcActivityManager; 251 final Context mContext; 252 253 /** 254 * A callback to be invoked when the system successfully delivers your {@link NdefMessage} 255 * to another device. 256 * @see #setOnNdefPushCompleteCallback 257 */ 258 public interface OnNdefPushCompleteCallback { 259 /** 260 * Called on successful NDEF push. 261 * 262 * <p>This callback is usually made on a binder thread (not the UI thread). 263 * 264 * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set 265 * @see #setNdefPushMessageCallback 266 */ 267 public void onNdefPushComplete(NfcEvent event); 268 } 269 270 /** 271 * A callback to be invoked when another NFC device capable of NDEF push (Android Beam) 272 * is within range. 273 * <p>Implement this interface and pass it to {@link 274 * NfcAdapter#setNdefPushMessageCallback setNdefPushMessageCallback()} in order to create an 275 * {@link NdefMessage} at the moment that another device is within range for NFC. Using this 276 * callback allows you to create a message with data that might vary based on the 277 * content currently visible to the user. Alternatively, you can call {@link 278 * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the 279 * same data. 280 */ 281 public interface CreateNdefMessageCallback { 282 /** 283 * Called to provide a {@link NdefMessage} to push. 284 * 285 * <p>This callback is usually made on a binder thread (not the UI thread). 286 * 287 * <p>Called when this device is in range of another device 288 * that might support NDEF push. It allows the application to 289 * create the NDEF message only when it is required. 290 * 291 * <p>NDEF push cannot occur until this method returns, so do not 292 * block for too long. 293 * 294 * <p>The Android operating system will usually show a system UI 295 * on top of your activity during this time, so do not try to request 296 * input from the user to complete the callback, or provide custom NDEF 297 * push UI. The user probably will not see it. 298 * 299 * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set 300 * @return NDEF message to push, or null to not provide a message 301 */ 302 public NdefMessage createNdefMessage(NfcEvent event); 303 } 304 305 306 // TODO javadoc 307 public interface CreateBeamUrisCallback { 308 public Uri[] createBeamUris(NfcEvent event); 309 } 310 311 /** 312 * Helper to check if this device has FEATURE_NFC, but without using 313 * a context. 314 * Equivalent to 315 * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) 316 */ 317 private static boolean hasNfcFeature() { 318 IPackageManager pm = ActivityThread.getPackageManager(); 319 if (pm == null) { 320 Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); 321 return false; 322 } 323 try { 324 return pm.hasSystemFeature(PackageManager.FEATURE_NFC); 325 } catch (RemoteException e) { 326 Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); 327 return false; 328 } 329 } 330 331 /** 332 * Returns the NfcAdapter for application context, 333 * or throws if NFC is not available. 334 * @hide 335 */ 336 public static synchronized NfcAdapter getNfcAdapter(Context context) { 337 if (!sIsInitialized) { 338 /* is this device meant to have NFC */ 339 if (!hasNfcFeature()) { 340 Log.v(TAG, "this device does not have NFC support"); 341 throw new UnsupportedOperationException(); 342 } 343 344 sService = getServiceInterface(); 345 if (sService == null) { 346 Log.e(TAG, "could not retrieve NFC service"); 347 throw new UnsupportedOperationException(); 348 } 349 try { 350 sTagService = sService.getNfcTagInterface(); 351 } catch (RemoteException e) { 352 Log.e(TAG, "could not retrieve NFC Tag service"); 353 throw new UnsupportedOperationException(); 354 } 355 356 sIsInitialized = true; 357 } 358 if (context == null) { 359 if (sNullContextNfcAdapter == null) { 360 sNullContextNfcAdapter = new NfcAdapter(null); 361 } 362 return sNullContextNfcAdapter; 363 } 364 NfcAdapter adapter = sNfcAdapters.get(context); 365 if (adapter == null) { 366 adapter = new NfcAdapter(context); 367 sNfcAdapters.put(context, adapter); 368 } 369 return adapter; 370 } 371 372 /** get handle to NFC service interface */ 373 private static INfcAdapter getServiceInterface() { 374 /* get a handle to NFC service */ 375 IBinder b = ServiceManager.getService("nfc"); 376 if (b == null) { 377 return null; 378 } 379 return INfcAdapter.Stub.asInterface(b); 380 } 381 382 /** 383 * Helper to get the default NFC Adapter. 384 * <p> 385 * Most Android devices will only have one NFC Adapter (NFC Controller). 386 * <p> 387 * This helper is the equivalent of: 388 * <pre>{@code 389 * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 390 * NfcAdapter adapter = manager.getDefaultAdapter(); 391 * }</pre> 392 * @param context the calling application's context 393 * 394 * @return the default NFC adapter, or null if no NFC adapter exists 395 */ 396 public static NfcAdapter getDefaultAdapter(Context context) { 397 if (context == null) { 398 throw new IllegalArgumentException("context cannot be null"); 399 } 400 context = context.getApplicationContext(); 401 if (context == null) { 402 throw new IllegalArgumentException( 403 "context not associated with any application (using a mock context?)"); 404 } 405 /* use getSystemService() for consistency */ 406 NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 407 if (manager == null) { 408 // NFC not available 409 return null; 410 } 411 return manager.getDefaultAdapter(); 412 } 413 414 /** 415 * Legacy NfcAdapter getter, always use {@link #getDefaultAdapter(Context)} instead.<p> 416 * This method was deprecated at API level 10 (Gingerbread MR1) because a context is required 417 * for many NFC API methods. Those methods will fail when called on an NfcAdapter 418 * object created from this method.<p> 419 * @deprecated use {@link #getDefaultAdapter(Context)} 420 * @hide 421 */ 422 @Deprecated 423 public static NfcAdapter getDefaultAdapter() { 424 // introduced in API version 9 (GB 2.3) 425 // deprecated in API version 10 (GB 2.3.3) 426 // removed from public API in version 16 (ICS MR2) 427 // should maintain as a hidden API for binary compatibility for a little longer 428 Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + 429 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); 430 431 return NfcAdapter.getNfcAdapter(null); 432 } 433 434 NfcAdapter(Context context) { 435 mContext = context; 436 mNfcActivityManager = new NfcActivityManager(this); 437 } 438 439 /** 440 * @hide 441 */ 442 public Context getContext() { 443 return mContext; 444 } 445 446 /** 447 * Returns the binder interface to the service. 448 * @hide 449 */ 450 public INfcAdapter getService() { 451 isEnabled(); // NOP call to recover sService if it is stale 452 return sService; 453 } 454 455 /** 456 * Returns the binder interface to the tag service. 457 * @hide 458 */ 459 public INfcTag getTagService() { 460 isEnabled(); // NOP call to recover sTagService if it is stale 461 return sTagService; 462 } 463 464 /** 465 * NFC service dead - attempt best effort recovery 466 * @hide 467 */ 468 public void attemptDeadServiceRecovery(Exception e) { 469 Log.e(TAG, "NFC service dead - attempting to recover", e); 470 INfcAdapter service = getServiceInterface(); 471 if (service == null) { 472 Log.e(TAG, "could not retrieve NFC service during service recovery"); 473 // nothing more can be done now, sService is still stale, we'll hit 474 // this recovery path again later 475 return; 476 } 477 // assigning to sService is not thread-safe, but this is best-effort code 478 // and on a well-behaved system should never happen 479 sService = service; 480 try { 481 sTagService = service.getNfcTagInterface(); 482 } catch (RemoteException ee) { 483 Log.e(TAG, "could not retrieve NFC tag service during service recovery"); 484 // nothing more can be done now, sService is still stale, we'll hit 485 // this recovery path again later 486 } 487 488 return; 489 } 490 491 /** 492 * Return true if this NFC Adapter has any features enabled. 493 * 494 * <p>If this method returns false, the NFC hardware is guaranteed not to 495 * generate or respond to any NFC communication over its NFC radio. 496 * <p>Applications can use this to check if NFC is enabled. Applications 497 * can request Settings UI allowing the user to toggle NFC using: 498 * <p><pre>startActivity(new Intent(Settings.ACTION_NFC_SETTINGS))</pre> 499 * 500 * @see android.provider.Settings#ACTION_NFC_SETTINGS 501 * @return true if this NFC Adapter has any features enabled 502 */ 503 public boolean isEnabled() { 504 try { 505 return sService.getState() == STATE_ON; 506 } catch (RemoteException e) { 507 attemptDeadServiceRecovery(e); 508 return false; 509 } 510 } 511 512 /** 513 * Return the state of this NFC Adapter. 514 * 515 * <p>Returns one of {@link #STATE_ON}, {@link #STATE_TURNING_ON}, 516 * {@link #STATE_OFF}, {@link #STATE_TURNING_OFF}. 517 * 518 * <p>{@link #isEnabled()} is equivalent to 519 * <code>{@link #getAdapterState()} == {@link #STATE_ON}</code> 520 * 521 * @return the current state of this NFC adapter 522 * 523 * @hide 524 */ 525 public int getAdapterState() { 526 try { 527 return sService.getState(); 528 } catch (RemoteException e) { 529 attemptDeadServiceRecovery(e); 530 return NfcAdapter.STATE_OFF; 531 } 532 } 533 534 /** 535 * Enable NFC hardware. 536 * 537 * <p>This call is asynchronous. Listen for 538 * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the 539 * operation is complete. 540 * 541 * <p>If this returns true, then either NFC is already on, or 542 * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent 543 * to indicate a state transition. If this returns false, then 544 * there is some problem that prevents an attempt to turn 545 * NFC on (for example we are in airplane mode and NFC is not 546 * toggleable in airplane mode on this platform). 547 * 548 * @hide 549 */ 550 public boolean enable() { 551 try { 552 return sService.enable(); 553 } catch (RemoteException e) { 554 attemptDeadServiceRecovery(e); 555 return false; 556 } 557 } 558 559 /** 560 * Disable NFC hardware. 561 * 562 * <p>No NFC features will work after this call, and the hardware 563 * will not perform or respond to any NFC communication. 564 * 565 * <p>This call is asynchronous. Listen for 566 * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the 567 * operation is complete. 568 * 569 * <p>If this returns true, then either NFC is already off, or 570 * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent 571 * to indicate a state transition. If this returns false, then 572 * there is some problem that prevents an attempt to turn 573 * NFC off. 574 * 575 * @hide 576 */ 577 578 public boolean disable() { 579 try { 580 return sService.disable(true); 581 } catch (RemoteException e) { 582 attemptDeadServiceRecovery(e); 583 return false; 584 } 585 } 586 587 //TODO: make sure NFC service has permission for URI 588 //TODO: see if we will eventually support multiple URIs 589 //TODO: javadoc 590 public void setBeamPushUris(Uri[] uris, Activity activity) { 591 if (activity == null) { 592 throw new NullPointerException("activity cannot be null"); 593 } 594 mNfcActivityManager.setNdefPushContentUri(activity, uris); 595 } 596 597 // TODO javadoc 598 public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) { 599 if (activity == null) { 600 throw new NullPointerException("activity cannot be null"); 601 } 602 mNfcActivityManager.setNdefPushContentUriCallback(activity, callback); 603 } 604 605 /** 606 * Set a static {@link NdefMessage} to send using Android Beam (TM). 607 * 608 * <p>This method may be called at any time before {@link Activity#onDestroy}, 609 * but the NDEF message is only made available for NDEF push when the 610 * specified activity(s) are in resumed (foreground) state. The recommended 611 * approach is to call this method during your Activity's 612 * {@link Activity#onCreate} - see sample 613 * code below. This method does not immediately perform any I/O or blocking work, 614 * so is safe to call on your main thread. 615 * 616 * <p>Only one NDEF message can be pushed by the currently resumed activity. 617 * If both {@link #setNdefPushMessage} and 618 * {@link #setNdefPushMessageCallback} are set, then 619 * the callback will take priority. 620 * 621 * <p>If neither {@link #setNdefPushMessage} or 622 * {@link #setNdefPushMessageCallback} have been called for your activity, then 623 * the Android OS may choose to send a default NDEF message on your behalf, 624 * such as a URI for your application. 625 * 626 * <p>If {@link #setNdefPushMessage} is called with a null NDEF message, 627 * and/or {@link #setNdefPushMessageCallback} is called with a null callback, 628 * then NDEF push will be completely disabled for the specified activity(s). 629 * This also disables any default NDEF message the Android OS would have 630 * otherwise sent on your behalf for those activity(s). 631 * 632 * <p>If you want to prevent the Android OS from sending default NDEF 633 * messages completely (for all activities), you can include a 634 * <code><meta-data></code> element inside the <code><application></code> 635 * element of your AndroidManifest.xml file, like this: 636 * <pre>{@code 637 * <application ...> 638 * <meta-data android:name="android.nfc.disable_beam_default" 639 * android:value="true" /> 640 * </application> 641 * }</pre> 642 * 643 * <p>The API allows for multiple activities to be specified at a time, 644 * but it is strongly recommended to just register one at a time, 645 * and to do so during the activity's {@link Activity#onCreate}. For example: 646 * <pre> 647 * protected void onCreate(Bundle savedInstanceState) { 648 * super.onCreate(savedInstanceState); 649 * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); 650 * if (nfcAdapter == null) return; // NFC not available on this device 651 * nfcAdapter.setNdefPushMessage(ndefMessage, this); 652 * } 653 * </pre> 654 * And that is it. Only one call per activity is necessary. The Android 655 * OS will automatically release its references to the NDEF message and the 656 * Activity object when it is destroyed if you follow this pattern. 657 * 658 * <p>If your Activity wants to dynamically generate an NDEF message, 659 * then set a callback using {@link #setNdefPushMessageCallback} instead 660 * of a static message. 661 * 662 * <p class="note">Do not pass in an Activity that has already been through 663 * {@link Activity#onDestroy}. This is guaranteed if you call this API 664 * during {@link Activity#onCreate}. 665 * 666 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 667 * 668 * @param message NDEF message to push over NFC, or null to disable 669 * @param activity activity for which the NDEF message will be pushed 670 * @param activities optional additional activities, however we strongly recommend 671 * to only register one at a time, and to do so in that activity's 672 * {@link Activity#onCreate} 673 */ 674 public void setNdefPushMessage(NdefMessage message, Activity activity, 675 Activity ... activities) { 676 int targetSdkVersion = getSdkVersion(); 677 try { 678 if (activity == null) { 679 throw new NullPointerException("activity cannot be null"); 680 } 681 mNfcActivityManager.setNdefPushMessage(activity, message); 682 for (Activity a : activities) { 683 if (a == null) { 684 throw new NullPointerException("activities cannot contain null"); 685 } 686 mNfcActivityManager.setNdefPushMessage(a, message); 687 } 688 } catch (IllegalStateException e) { 689 if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) { 690 // Less strict on old applications - just log the error 691 Log.e(TAG, "Cannot call API with Activity that has already " + 692 "been destroyed", e); 693 } else { 694 // Prevent new applications from making this mistake, re-throw 695 throw(e); 696 } 697 } 698 } 699 700 /** 701 * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM). 702 * 703 * <p>This method may be called at any time before {@link Activity#onDestroy}, 704 * but the NDEF message callback can only occur when the 705 * specified activity(s) are in resumed (foreground) state. The recommended 706 * approach is to call this method during your Activity's 707 * {@link Activity#onCreate} - see sample 708 * code below. This method does not immediately perform any I/O or blocking work, 709 * so is safe to call on your main thread. 710 * 711 * <p>Only one NDEF message can be pushed by the currently resumed activity. 712 * If both {@link #setNdefPushMessage} and 713 * {@link #setNdefPushMessageCallback} are set, then 714 * the callback will take priority. 715 * 716 * <p>If neither {@link #setNdefPushMessage} or 717 * {@link #setNdefPushMessageCallback} have been called for your activity, then 718 * the Android OS may choose to send a default NDEF message on your behalf, 719 * such as a URI for your application. 720 * 721 * <p>If {@link #setNdefPushMessage} is called with a null NDEF message, 722 * and/or {@link #setNdefPushMessageCallback} is called with a null callback, 723 * then NDEF push will be completely disabled for the specified activity(s). 724 * This also disables any default NDEF message the Android OS would have 725 * otherwise sent on your behalf for those activity(s). 726 * 727 * <p>If you want to prevent the Android OS from sending default NDEF 728 * messages completely (for all activities), you can include a 729 * <code><meta-data></code> element inside the <code><application></code> 730 * element of your AndroidManifest.xml file, like this: 731 * <pre>{@code 732 * <application ...> 733 * <meta-data android:name="android.nfc.disable_beam_default" 734 * android:value="true" /> 735 * </application> 736 * }</pre> 737 * 738 * <p>The API allows for multiple activities to be specified at a time, 739 * but it is strongly recommended to just register one at a time, 740 * and to do so during the activity's {@link Activity#onCreate}. For example: 741 * <pre> 742 * protected void onCreate(Bundle savedInstanceState) { 743 * super.onCreate(savedInstanceState); 744 * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); 745 * if (nfcAdapter == null) return; // NFC not available on this device 746 * nfcAdapter.setNdefPushMessageCallback(callback, this); 747 * } 748 * </pre> 749 * And that is it. Only one call per activity is necessary. The Android 750 * OS will automatically release its references to the callback and the 751 * Activity object when it is destroyed if you follow this pattern. 752 * 753 * <p class="note">Do not pass in an Activity that has already been through 754 * {@link Activity#onDestroy}. This is guaranteed if you call this API 755 * during {@link Activity#onCreate}. 756 * 757 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 758 * 759 * @param callback callback, or null to disable 760 * @param activity activity for which the NDEF message will be pushed 761 * @param activities optional additional activities, however we strongly recommend 762 * to only register one at a time, and to do so in that activity's 763 * {@link Activity#onCreate} 764 */ 765 public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, 766 Activity ... activities) { 767 int targetSdkVersion = getSdkVersion(); 768 try { 769 if (activity == null) { 770 throw new NullPointerException("activity cannot be null"); 771 } 772 mNfcActivityManager.setNdefPushMessageCallback(activity, callback); 773 for (Activity a : activities) { 774 if (a == null) { 775 throw new NullPointerException("activities cannot contain null"); 776 } 777 mNfcActivityManager.setNdefPushMessageCallback(a, callback); 778 } 779 } catch (IllegalStateException e) { 780 if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) { 781 // Less strict on old applications - just log the error 782 Log.e(TAG, "Cannot call API with Activity that has already " + 783 "been destroyed", e); 784 } else { 785 // Prevent new applications from making this mistake, re-throw 786 throw(e); 787 } 788 } 789 } 790 791 /** 792 * Set a callback on successful Android Beam (TM). 793 * 794 * <p>This method may be called at any time before {@link Activity#onDestroy}, 795 * but the callback can only occur when the 796 * specified activity(s) are in resumed (foreground) state. The recommended 797 * approach is to call this method during your Activity's 798 * {@link Activity#onCreate} - see sample 799 * code below. This method does not immediately perform any I/O or blocking work, 800 * so is safe to call on your main thread. 801 * 802 * <p>The API allows for multiple activities to be specified at a time, 803 * but it is strongly recommended to just register one at a time, 804 * and to do so during the activity's {@link Activity#onCreate}. For example: 805 * <pre> 806 * protected void onCreate(Bundle savedInstanceState) { 807 * super.onCreate(savedInstanceState); 808 * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); 809 * if (nfcAdapter == null) return; // NFC not available on this device 810 * nfcAdapter.setOnNdefPushCompleteCallback(callback, this); 811 * } 812 * </pre> 813 * And that is it. Only one call per activity is necessary. The Android 814 * OS will automatically release its references to the callback and the 815 * Activity object when it is destroyed if you follow this pattern. 816 * 817 * <p class="note">Do not pass in an Activity that has already been through 818 * {@link Activity#onDestroy}. This is guaranteed if you call this API 819 * during {@link Activity#onCreate}. 820 * 821 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 822 * 823 * @param callback callback, or null to disable 824 * @param activity activity for which the NDEF message will be pushed 825 * @param activities optional additional activities, however we strongly recommend 826 * to only register one at a time, and to do so in that activity's 827 * {@link Activity#onCreate} 828 */ 829 public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, 830 Activity activity, Activity ... activities) { 831 int targetSdkVersion = getSdkVersion(); 832 try { 833 if (activity == null) { 834 throw new NullPointerException("activity cannot be null"); 835 } 836 mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback); 837 for (Activity a : activities) { 838 if (a == null) { 839 throw new NullPointerException("activities cannot contain null"); 840 } 841 mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback); 842 } 843 } catch (IllegalStateException e) { 844 if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) { 845 // Less strict on old applications - just log the error 846 Log.e(TAG, "Cannot call API with Activity that has already " + 847 "been destroyed", e); 848 } else { 849 // Prevent new applications from making this mistake, re-throw 850 throw(e); 851 } 852 } 853 } 854 855 /** 856 * Enable foreground dispatch to the given Activity. 857 * 858 * <p>This will give give priority to the foreground activity when 859 * dispatching a discovered {@link Tag} to an application. 860 * 861 * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents 862 * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and 863 * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED} 864 * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled 865 * by passing in the tech lists separately. Each first level entry in the tech list represents 866 * an array of technologies that must all be present to match. If any of the first level sets 867 * match then the dispatch is routed through the given PendingIntent. In other words, the second 868 * level is ANDed together and the first level entries are ORed together. 869 * 870 * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters 871 * that acts a wild card and will cause the foreground activity to receive all tags via the 872 * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent. 873 * 874 * <p>This method must be called from the main thread, and only when the activity is in the 875 * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before 876 * the completion of their {@link Activity#onPause} callback to disable foreground dispatch 877 * after it has been enabled. 878 * 879 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 880 * 881 * @param activity the Activity to dispatch to 882 * @param intent the PendingIntent to start for the dispatch 883 * @param filters the IntentFilters to override dispatching for, or null to always dispatch 884 * @param techLists the tech lists used to perform matching for dispatching of the 885 * {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent 886 * @throws IllegalStateException if the Activity is not currently in the foreground 887 */ 888 public void enableForegroundDispatch(Activity activity, PendingIntent intent, 889 IntentFilter[] filters, String[][] techLists) { 890 if (activity == null || intent == null) { 891 throw new NullPointerException(); 892 } 893 if (!activity.isResumed()) { 894 throw new IllegalStateException("Foreground dispatch can only be enabled " + 895 "when your activity is resumed"); 896 } 897 try { 898 TechListParcel parcel = null; 899 if (techLists != null && techLists.length > 0) { 900 parcel = new TechListParcel(techLists); 901 } 902 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 903 mForegroundDispatchListener); 904 sService.setForegroundDispatch(intent, filters, parcel); 905 } catch (RemoteException e) { 906 attemptDeadServiceRecovery(e); 907 } 908 } 909 910 /** 911 * Disable foreground dispatch to the given activity. 912 * 913 * <p>After calling {@link #enableForegroundDispatch}, an activity 914 * must call this method before its {@link Activity#onPause} callback 915 * completes. 916 * 917 * <p>This method must be called from the main thread. 918 * 919 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 920 * 921 * @param activity the Activity to disable dispatch to 922 * @throws IllegalStateException if the Activity has already been paused 923 */ 924 public void disableForegroundDispatch(Activity activity) { 925 ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, 926 mForegroundDispatchListener); 927 disableForegroundDispatchInternal(activity, false); 928 } 929 930 OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() { 931 @Override 932 public void onPaused(Activity activity) { 933 disableForegroundDispatchInternal(activity, true); 934 } 935 }; 936 937 void disableForegroundDispatchInternal(Activity activity, boolean force) { 938 try { 939 sService.setForegroundDispatch(null, null, null); 940 if (!force && !activity.isResumed()) { 941 throw new IllegalStateException("You must disable foreground dispatching " + 942 "while your activity is still resumed"); 943 } 944 } catch (RemoteException e) { 945 attemptDeadServiceRecovery(e); 946 } 947 } 948 949 /** 950 * Enable NDEF message push over NFC while this Activity is in the foreground. 951 * 952 * <p>You must explicitly call this method every time the activity is 953 * resumed, and you must call {@link #disableForegroundNdefPush} before 954 * your activity completes {@link Activity#onPause}. 955 * 956 * <p>Strongly recommend to use the new {@link #setNdefPushMessage} 957 * instead: it automatically hooks into your activity life-cycle, 958 * so you do not need to call enable/disable in your onResume/onPause. 959 * 960 * <p>For NDEF push to function properly the other NFC device must 961 * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or 962 * Android's "com.android.npp" (Ndef Push Protocol). This was optional 963 * on Gingerbread level Android NFC devices, but SNEP is mandatory on 964 * Ice-Cream-Sandwich and beyond. 965 * 966 * <p>This method must be called from the main thread. 967 * 968 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 969 * 970 * @param activity foreground activity 971 * @param message a NDEF Message to push over NFC 972 * @throws IllegalStateException if the activity is not currently in the foreground 973 * @deprecated use {@link #setNdefPushMessage} instead 974 */ 975 @Deprecated 976 public void enableForegroundNdefPush(Activity activity, NdefMessage message) { 977 if (activity == null || message == null) { 978 throw new NullPointerException(); 979 } 980 enforceResumed(activity); 981 mNfcActivityManager.setNdefPushMessage(activity, message); 982 } 983 984 /** 985 * Disable NDEF message push over P2P. 986 * 987 * <p>After calling {@link #enableForegroundNdefPush}, an activity 988 * must call this method before its {@link Activity#onPause} callback 989 * completes. 990 * 991 * <p>Strongly recommend to use the new {@link #setNdefPushMessage} 992 * instead: it automatically hooks into your activity life-cycle, 993 * so you do not need to call enable/disable in your onResume/onPause. 994 * 995 * <p>This method must be called from the main thread. 996 * 997 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 998 * 999 * @param activity the Foreground activity 1000 * @throws IllegalStateException if the Activity has already been paused 1001 * @deprecated use {@link #setNdefPushMessage} instead 1002 */ 1003 @Deprecated 1004 public void disableForegroundNdefPush(Activity activity) { 1005 if (activity == null) { 1006 throw new NullPointerException(); 1007 } 1008 enforceResumed(activity); 1009 mNfcActivityManager.setNdefPushMessage(activity, null); 1010 mNfcActivityManager.setNdefPushMessageCallback(activity, null); 1011 mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null); 1012 } 1013 1014 /** 1015 * Enable NDEF Push feature. 1016 * <p>This API is for the Settings application. 1017 * @hide 1018 */ 1019 public boolean enableNdefPush() { 1020 try { 1021 return sService.enableNdefPush(); 1022 } catch (RemoteException e) { 1023 attemptDeadServiceRecovery(e); 1024 return false; 1025 } 1026 } 1027 1028 /** 1029 * Disable NDEF Push feature. 1030 * <p>This API is for the Settings application. 1031 * @hide 1032 */ 1033 public boolean disableNdefPush() { 1034 try { 1035 return sService.disableNdefPush(); 1036 } catch (RemoteException e) { 1037 attemptDeadServiceRecovery(e); 1038 return false; 1039 } 1040 } 1041 1042 /** 1043 * Return true if the NDEF Push (Android Beam) feature is enabled. 1044 * <p>This function will return true only if both NFC is enabled, and the 1045 * NDEF Push feature is enabled. 1046 * <p>Note that if NFC is enabled but NDEF Push is disabled then this 1047 * device can still <i>receive</i> NDEF messages, it just cannot send them. 1048 * <p>Applications cannot directly toggle the NDEF Push feature, but they 1049 * can request Settings UI allowing the user to toggle NDEF Push using 1050 * <code>startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS))</code> 1051 * <p>Example usage in an Activity that requires NDEF Push: 1052 * <p><pre> 1053 * protected void onResume() { 1054 * super.onResume(); 1055 * if (!nfcAdapter.isEnabled()) { 1056 * startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); 1057 * } else if (!nfcAdapter.isNdefPushEnabled()) { 1058 * startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS)); 1059 * } 1060 * } 1061 * </pre> 1062 * 1063 * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS 1064 * @return true if NDEF Push feature is enabled 1065 */ 1066 public boolean isNdefPushEnabled() { 1067 try { 1068 return sService.isNdefPushEnabled(); 1069 } catch (RemoteException e) { 1070 attemptDeadServiceRecovery(e); 1071 return false; 1072 } 1073 } 1074 1075 /** 1076 * Inject a mock NFC tag.<p> 1077 * Used for testing purposes. 1078 * <p class="note">Requires the 1079 * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission. 1080 * @hide 1081 */ 1082 public void dispatch(Tag tag) { 1083 if (tag == null) { 1084 throw new NullPointerException("tag cannot be null"); 1085 } 1086 try { 1087 sService.dispatch(tag); 1088 } catch (RemoteException e) { 1089 attemptDeadServiceRecovery(e); 1090 } 1091 } 1092 1093 /** 1094 * @hide 1095 */ 1096 public void setP2pModes(int initiatorModes, int targetModes) { 1097 try { 1098 sService.setP2pModes(initiatorModes, targetModes); 1099 } catch (RemoteException e) { 1100 attemptDeadServiceRecovery(e); 1101 } 1102 } 1103 1104 /** 1105 * @hide 1106 */ 1107 public INfcAdapterExtras getNfcAdapterExtrasInterface() { 1108 if (mContext == null) { 1109 throw new UnsupportedOperationException("You need a context on NfcAdapter to use the " 1110 + " NFC extras APIs"); 1111 } 1112 try { 1113 return sService.getNfcAdapterExtrasInterface(mContext.getPackageName()); 1114 } catch (RemoteException e) { 1115 attemptDeadServiceRecovery(e); 1116 return null; 1117 } 1118 } 1119 1120 void enforceResumed(Activity activity) { 1121 if (!activity.isResumed()) { 1122 throw new IllegalStateException("API cannot be called while activity is paused"); 1123 } 1124 } 1125 1126 int getSdkVersion() { 1127 if (mContext == null) { 1128 return android.os.Build.VERSION_CODES.GINGERBREAD; // best guess 1129 } else { 1130 return mContext.getApplicationInfo().targetSdkVersion; 1131 } 1132 } 1133} 1134