NfcAdapter.java revision 562dfff65ac79590c0b7c50019cd3d107e8b4e0e
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.Activity; 22import android.app.ActivityThread; 23import android.app.OnActivityPausedListener; 24import android.app.PendingIntent; 25import android.content.Context; 26import android.content.IntentFilter; 27import android.content.pm.IPackageManager; 28import android.content.pm.PackageManager; 29import android.nfc.tech.MifareClassic; 30import android.nfc.tech.Ndef; 31import android.nfc.tech.NfcA; 32import android.nfc.tech.NfcF; 33import android.os.IBinder; 34import android.os.RemoteException; 35import android.os.ServiceManager; 36import android.util.Log; 37 38/** 39 * Represents the local NFC adapter. 40 * <p> 41 * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC 42 * adapter for this Android device. 43 */ 44public final class NfcAdapter { 45 static final String TAG = "NFC"; 46 47 /** 48 * Intent to start an activity when a tag with NDEF payload is discovered. 49 * 50 * <p>The system inspects the first {@link NdefRecord} in the first {@link NdefMessage} and 51 * looks for a URI, SmartPoster, or MIME record. If a URI or SmartPoster record is found the 52 * intent will contain the URI in its data field. If a MIME record is found the intent will 53 * contain the MIME type in its type field. This allows activities to register 54 * {@link IntentFilter}s targeting specific content on tags. Activities should register the 55 * most specific intent filters possible to avoid the activity chooser dialog, which can 56 * disrupt the interaction with the tag as the user interacts with the screen. 57 * 58 * <p>If the tag has an NDEF payload this intent is started before 59 * {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither 60 * {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started. 61 */ 62 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 63 public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; 64 65 /** 66 * Intent to start an activity when a tag is discovered and activities are registered for the 67 * specific technologies on the tag. 68 * 69 * <p>To receive this intent an activity must include an intent filter 70 * for this action and specify the desired tech types in a 71 * manifest <code>meta-data</code> entry. Here is an example manfiest entry: 72 * <pre> 73 * <activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"> 74 * <!-- Add a technology filter --> 75 * <intent-filter> 76 * <action android:name="android.nfc.action.TECH_DISCOVERED" /> 77 * </intent-filter> 78 * 79 * <meta-data android:name="android.nfc.action.TECH_DISCOVERED" 80 * android:resource="@xml/filter_nfc" 81 * /> 82 * </activity> 83 * </pre> 84 * 85 * <p>The meta-data XML file should contain one or more <code>tech-list</code> entries 86 * each consisting or one or more <code>tech</code> entries. The <code>tech</code> entries refer 87 * to the qualified class name implementing the technology, for example "android.nfc.tech.NfcA". 88 * 89 * <p>A tag matches if any of the 90 * <code>tech-list</code> sets is a subset of {@link Tag#getTechList() Tag.getTechList()}. Each 91 * of the <code>tech-list</code>s is considered independently and the 92 * activity is considered a match is any single <code>tech-list</code> matches the tag that was 93 * discovered. This provides AND and OR semantics for filtering desired techs. Here is an 94 * example that will match any tag using {@link NfcF} or any tag using {@link NfcA}, 95 * {@link MifareClassic}, and {@link Ndef}: 96 * 97 * <pre> 98 * <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 99 * <!-- capture anything using NfcF --> 100 * <tech-list> 101 * <tech>android.nfc.tech.NfcF</tech> 102 * </tech-list> 103 * 104 * <!-- OR --> 105 * 106 * <!-- capture all MIFARE Classics with NDEF payloads --> 107 * <tech-list> 108 * <tech>android.nfc.tech.NfcA</tech> 109 * <tech>android.nfc.tech.MifareClassic</tech> 110 * <tech>android.nfc.tech.Ndef</tech> 111 * </tech-list> 112 * </resources> 113 * </pre> 114 * 115 * <p>This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before 116 * {@link #ACTION_TAG_DISCOVERED}. If any activities respond to {@link #ACTION_NDEF_DISCOVERED} 117 * this intent will not be started. If any activities respond to this intent 118 * {@link #ACTION_TAG_DISCOVERED} will not be started. 119 */ 120 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 121 public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED"; 122 123 /** 124 * Intent to start an activity when a tag is discovered. 125 * 126 * <p>This intent will not be started when a tag is discovered if any activities respond to 127 * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. 128 */ 129 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 130 public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; 131 132 /** 133 * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED 134 * @hide 135 */ 136 public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST"; 137 138 /** 139 * Mandatory extra containing the {@link Tag} that was discovered for the 140 * {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and 141 * {@link #ACTION_TAG_DISCOVERED} intents. 142 */ 143 public static final String EXTRA_TAG = "android.nfc.extra.TAG"; 144 145 /** 146 * Optional extra containing an array of {@link NdefMessage} present on the discovered tag for 147 * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and 148 * {@link #ACTION_TAG_DISCOVERED} intents. 149 */ 150 public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; 151 152 /** 153 * Optional extra containing a byte array containing the ID of the discovered tag for 154 * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and 155 * {@link #ACTION_TAG_DISCOVERED} intents. 156 */ 157 public static final String EXTRA_ID = "android.nfc.extra.ID"; 158 159 /** 160 * Broadcast Action: The state of the local NFC adapter has been 161 * changed. 162 * <p>For example, NFC has been turned on or off. 163 * <p>Always contains the extra field {@link #EXTRA_STATE} 164 * @hide 165 */ 166 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 167 public static final String ACTION_ADAPTER_STATE_CHANGED = 168 "android.nfc.action.ADAPTER_STATE_CHANGED"; 169 170 /** 171 * Used as an int extra field in {@link #ACTION_STATE_CHANGED} 172 * intents to request the current power state. Possible values are: 173 * {@link #STATE_OFF}, 174 * {@link #STATE_TURNING_ON}, 175 * {@link #STATE_ON}, 176 * {@link #STATE_TURNING_OFF}, 177 * @hide 178 */ 179 public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE"; 180 181 /** @hide */ 182 public static final int STATE_OFF = 1; 183 /** @hide */ 184 public static final int STATE_TURNING_ON = 2; 185 /** @hide */ 186 public static final int STATE_ON = 3; 187 /** @hide */ 188 public static final int STATE_TURNING_OFF = 4; 189 190 // Guarded by NfcAdapter.class 191 static boolean sIsInitialized = false; 192 193 // Final after first constructor, except for 194 // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort 195 // recovery 196 static INfcAdapter sService; 197 static INfcTag sTagService; 198 199 /** 200 * NfcAdapter is currently a singleton, and does not require a context. 201 * However all the public API's are future-proofed to require a context. 202 * If we start using that then we'll need to keep a HashMap of 203 * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter 204 * is a singleton within each application context. 205 */ 206 static NfcAdapter sSingleton; // protected by NfcAdapter.class 207 208 final NfcActivityManager mNfcActivityManager; 209 210 /** 211 * @see {@link #setNdefPushMessageCallback} 212 */ 213 public interface OnNdefPushCompleteCallback { 214 /** 215 * Called on successful NDEF push. 216 * 217 * <p>This callback is usually made on a binder thread (not the UI thread). 218 * 219 * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set 220 * @see {@link #setNdefPushMessageCallback} 221 */ 222 public void onNdefPushComplete(NfcEvent event); 223 } 224 225 /** 226 * @see {@link #setCeateNdefMessageCallback} 227 */ 228 public interface CreateNdefMessageCallback { 229 /** 230 * Called to provide a {@link NdefMessage} to push. 231 * 232 * <p>This callback is usually made on a binder thread (not the UI thread). 233 * 234 * <p>Called when this device is in range of another device 235 * that might support NDEF push. It allows the application to 236 * create the NDEF message only when it is required. 237 * 238 * <p>NDEF push cannot occur until this method returns, so do not 239 * block for too long. 240 * 241 * <p>The Android operating system will usually show a system UI 242 * on top of your activity during this time, so do not try to request 243 * input from the user to complete the callback, or provide custom NDEF 244 * push UI. The user probably will not see it. 245 * 246 * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set 247 * @return NDEF message to push, or null to not provide a message 248 */ 249 public NdefMessage createNdefMessage(NfcEvent event); 250 } 251 252 /** 253 * Helper to check if this device has FEATURE_NFC, but without using 254 * a context. 255 * Equivalent to 256 * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) 257 */ 258 private static boolean hasNfcFeature() { 259 IPackageManager pm = ActivityThread.getPackageManager(); 260 if (pm == null) { 261 Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); 262 return false; 263 } 264 try { 265 return pm.hasSystemFeature(PackageManager.FEATURE_NFC); 266 } catch (RemoteException e) { 267 Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); 268 return false; 269 } 270 } 271 272 /** 273 * Returns the singleton, or throws if NFC is not available. 274 */ 275 static synchronized NfcAdapter getSingleton() { 276 if (!sIsInitialized) { 277 sIsInitialized = true; 278 279 /* is this device meant to have NFC */ 280 if (!hasNfcFeature()) { 281 Log.v(TAG, "this device does not have NFC support"); 282 throw new UnsupportedOperationException(); 283 } 284 285 sService = getServiceInterface(); 286 if (sService == null) { 287 Log.e(TAG, "could not retrieve NFC service"); 288 throw new UnsupportedOperationException(); 289 } 290 try { 291 sTagService = sService.getNfcTagInterface(); 292 } catch (RemoteException e) { 293 Log.e(TAG, "could not retrieve NFC Tag service"); 294 throw new UnsupportedOperationException(); 295 } 296 sSingleton = new NfcAdapter(); 297 } 298 if (sSingleton == null) { 299 throw new UnsupportedOperationException(); 300 } 301 return sSingleton; 302 } 303 304 /** get handle to NFC service interface */ 305 private static INfcAdapter getServiceInterface() { 306 /* get a handle to NFC service */ 307 IBinder b = ServiceManager.getService("nfc"); 308 if (b == null) { 309 return null; 310 } 311 return INfcAdapter.Stub.asInterface(b); 312 } 313 314 /** 315 * Helper to get the default NFC Adapter. 316 * <p> 317 * Most Android devices will only have one NFC Adapter (NFC Controller). 318 * <p> 319 * This helper is the equivalent of: 320 * <pre>{@code 321 * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 322 * NfcAdapter adapter = manager.getDefaultAdapter(); 323 * }</pre> 324 * @param context the calling application's context 325 * 326 * @return the default NFC adapter, or null if no NFC adapter exists 327 */ 328 public static NfcAdapter getDefaultAdapter(Context context) { 329 /* use getSystemService() instead of just instantiating to take 330 * advantage of the context's cached NfcManager & NfcAdapter */ 331 NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 332 return manager.getDefaultAdapter(); 333 } 334 335 /** 336 * Get a handle to the default NFC Adapter on this Android device. 337 * <p> 338 * Most Android devices will only have one NFC Adapter (NFC Controller). 339 * 340 * @return the default NFC adapter, or null if no NFC adapter exists 341 * @deprecated use {@link #getDefaultAdapter(Context)} 342 */ 343 @Deprecated 344 public static NfcAdapter getDefaultAdapter() { 345 Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + 346 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); 347 return getSingleton(); 348 } 349 350 /** 351 * Does not currently need a context. 352 */ 353 NfcAdapter() { 354 mNfcActivityManager = new NfcActivityManager(this); 355 } 356 357 /** 358 * Returns the binder interface to the service. 359 * @hide 360 */ 361 public INfcAdapter getService() { 362 isEnabled(); // NOP call to recover sService if it is stale 363 return sService; 364 } 365 366 /** 367 * Returns the binder interface to the tag service. 368 * @hide 369 */ 370 public INfcTag getTagService() { 371 isEnabled(); // NOP call to recover sTagService if it is stale 372 return sTagService; 373 } 374 375 /** 376 * NFC service dead - attempt best effort recovery 377 * @hide 378 */ 379 public void attemptDeadServiceRecovery(Exception e) { 380 Log.e(TAG, "NFC service dead - attempting to recover", e); 381 INfcAdapter service = getServiceInterface(); 382 if (service == null) { 383 Log.e(TAG, "could not retrieve NFC service during service recovery"); 384 // nothing more can be done now, sService is still stale, we'll hit 385 // this recovery path again later 386 return; 387 } 388 // assigning to sService is not thread-safe, but this is best-effort code 389 // and on a well-behaved system should never happen 390 sService = service; 391 try { 392 sTagService = service.getNfcTagInterface(); 393 } catch (RemoteException ee) { 394 Log.e(TAG, "could not retrieve NFC tag service during service recovery"); 395 // nothing more can be done now, sService is still stale, we'll hit 396 // this recovery path again later 397 } 398 399 return; 400 } 401 402 /** 403 * Return true if this NFC Adapter has any features enabled. 404 * 405 * <p>Application may use this as a helper to suggest that the user 406 * should turn on NFC in Settings. 407 * <p>If this method returns false, the NFC hardware is guaranteed not to 408 * generate or respond to any NFC transactions. 409 * 410 * @return true if this NFC Adapter has any features enabled 411 */ 412 public boolean isEnabled() { 413 try { 414 return sService.getState() == STATE_ON; 415 } catch (RemoteException e) { 416 attemptDeadServiceRecovery(e); 417 return false; 418 } 419 } 420 421 /** 422 * Return the state of this NFC Adapter. 423 * 424 * <p>Returns one of {@link #STATE_ON}, {@link #STATE_TURNING_ON}, 425 * {@link #STATE_OFF}, {@link #STATE_TURNING_OFF}. 426 * 427 * <p>{@link #isEnabled()} is equivalent to 428 * <code>{@link #getAdapterState()} == {@link #STATE_ON}</code> 429 * 430 * @return the current state of this NFC adapter 431 * 432 * @hide 433 */ 434 public int getAdapterState() { 435 try { 436 return sService.getState(); 437 } catch (RemoteException e) { 438 attemptDeadServiceRecovery(e); 439 return NfcAdapter.STATE_OFF; 440 } 441 } 442 443 /** 444 * Enable NFC hardware. 445 * 446 * <p>This call is asynchronous. Listen for 447 * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the 448 * operation is complete. 449 * 450 * <p>If this returns true, then either NFC is already on, or 451 * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent 452 * to indicate a state transition. If this returns false, then 453 * there is some problem that prevents an attempt to turn 454 * NFC on (for example we are in airplane mode and NFC is not 455 * toggleable in airplane mode on this platform). 456 * 457 * @hide 458 */ 459 public boolean enable() { 460 try { 461 return sService.enable(); 462 } catch (RemoteException e) { 463 attemptDeadServiceRecovery(e); 464 return false; 465 } 466 } 467 468 /** 469 * Disable NFC hardware. 470 * 471 * <p>No NFC features will work after this call, and the hardware 472 * will not perform or respond to any NFC communication. 473 * 474 * <p>This call is asynchronous. Listen for 475 * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the 476 * operation is complete. 477 * 478 * <p>If this returns true, then either NFC is already off, or 479 * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent 480 * to indicate a state transition. If this returns false, then 481 * there is some problem that prevents an attempt to turn 482 * NFC off. 483 * 484 * @hide 485 */ 486 public boolean disable() { 487 try { 488 return sService.disable(); 489 } catch (RemoteException e) { 490 attemptDeadServiceRecovery(e); 491 return false; 492 } 493 } 494 495 /** 496 * Set the {@link NdefMessage} to push over NFC during the specified activities. 497 * 498 * <p>This method may be called at any time, but the NDEF message is 499 * only made available for NDEF push when one of the specified activities 500 * is in resumed (foreground) state. 501 * 502 * <p>Only one NDEF message can be pushed by the currently resumed activity. 503 * If both {@link #setNdefPushMessage} and 504 * {@link #setNdefPushMessageCallback} are set then 505 * the callback will take priority. 506 * 507 * <p>Pass a null NDEF message to disable foreground NDEF push in the 508 * specified activities. 509 * 510 * <p>One or more activities must be specified. 511 * 512 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 513 * 514 * @param message NDEF message to push over NFC, or null to disable 515 * @param activity an activity to enable for NDEF push (at least one is required) 516 * @param activities zero or more additional activities to enable for NDEF Push 517 */ 518 public void setNdefPushMessage(NdefMessage message, Activity activity, 519 Activity ... activities) { 520 if (activity == null) { 521 throw new NullPointerException("activity cannot be null"); 522 } 523 mNfcActivityManager.setNdefPushMessage(activity, message); 524 for (Activity a : activities) { 525 if (a == null) { 526 throw new NullPointerException("activities cannot contain null"); 527 } 528 mNfcActivityManager.setNdefPushMessage(a, message); 529 } 530 } 531 532 /** 533 * Set the callback to create a {@link NdefMessage} to push over NFC. 534 * 535 * <p>This method may be called at any time, but this callback is 536 * only made if one of the specified activities 537 * is in resumed (foreground) state. 538 * 539 * <p>Only one NDEF message can be pushed by the currently resumed activity. 540 * If both {@link #setNdefPushMessage} and 541 * {@link #setNdefPushMessageCallback} are set then 542 * the callback will take priority. 543 * 544 * <p>Pass a null callback to disable the callback in the 545 * specified activities. 546 * 547 * <p>One or more activities must be specified. 548 * 549 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 550 * 551 * @param callback callback, or null to disable 552 * @param activity an activity to enable for NDEF push (at least one is required) 553 * @param activities zero or more additional activities to enable for NDEF Push 554 */ 555 public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity, 556 Activity ... activities) { 557 if (activity == null) { 558 throw new NullPointerException("activity cannot be null"); 559 } 560 mNfcActivityManager.setNdefPushMessageCallback(activity, callback); 561 for (Activity a : activities) { 562 if (a == null) { 563 throw new NullPointerException("activities cannot contain null"); 564 } 565 mNfcActivityManager.setNdefPushMessageCallback(a, callback); 566 } 567 } 568 569 /** 570 * Set the callback on a successful NDEF push over NFC. 571 * 572 * <p>This method may be called at any time, but NDEF push and this callback 573 * can only occur when one of the specified activities is in resumed 574 * (foreground) state. 575 * 576 * <p>One or more activities must be specified. 577 * 578 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 579 * 580 * @param callback callback, or null to disable 581 * @param activity an activity to enable the callback (at least one is required) 582 * @param activities zero or more additional activities to enable to callback 583 */ 584 public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback, 585 Activity activity, Activity ... activities) { 586 if (activity == null) { 587 throw new NullPointerException("activity cannot be null"); 588 } 589 mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback); 590 for (Activity a : activities) { 591 if (a == null) { 592 throw new NullPointerException("activities cannot contain null"); 593 } 594 mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback); 595 } 596 } 597 598 /** 599 * Enable foreground dispatch to the given Activity. 600 * 601 * <p>This will give give priority to the foreground activity when 602 * dispatching a discovered {@link Tag} to an application. 603 * 604 * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents 605 * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and 606 * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED} 607 * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled 608 * by passing in the tech lists separately. Each first level entry in the tech list represents 609 * an array of technologies that must all be present to match. If any of the first level sets 610 * match then the dispatch is routed through the given PendingIntent. In other words, the second 611 * level is ANDed together and the first level entries are ORed together. 612 * 613 * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters 614 * that acts a wild card and will cause the foreground activity to receive all tags via the 615 * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent. 616 * 617 * <p>This method must be called from the main thread, and only when the activity is in the 618 * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before 619 * the completion of their {@link Activity#onPause} callback to disable foreground dispatch 620 * after it has been enabled. 621 * 622 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 623 * 624 * @param activity the Activity to dispatch to 625 * @param intent the PendingIntent to start for the dispatch 626 * @param filters the IntentFilters to override dispatching for, or null to always dispatch 627 * @param techLists the tech lists used to perform matching for dispatching of the 628 * {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent 629 * @throws IllegalStateException if the Activity is not currently in the foreground 630 */ 631 public void enableForegroundDispatch(Activity activity, PendingIntent intent, 632 IntentFilter[] filters, String[][] techLists) { 633 if (activity == null || intent == null) { 634 throw new NullPointerException(); 635 } 636 if (!activity.isResumed()) { 637 throw new IllegalStateException("Foreground dispatch can only be enabled " + 638 "when your activity is resumed"); 639 } 640 try { 641 TechListParcel parcel = null; 642 if (techLists != null && techLists.length > 0) { 643 parcel = new TechListParcel(techLists); 644 } 645 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 646 mForegroundDispatchListener); 647 sService.setForegroundDispatch(intent, filters, parcel); 648 } catch (RemoteException e) { 649 attemptDeadServiceRecovery(e); 650 } 651 } 652 653 /** 654 * Disable foreground dispatch to the given activity. 655 * 656 * <p>After calling {@link #enableForegroundDispatch}, an activity 657 * must call this method before its {@link Activity#onPause} callback 658 * completes. 659 * 660 * <p>This method must be called from the main thread. 661 * 662 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 663 * 664 * @param activity the Activity to disable dispatch to 665 * @throws IllegalStateException if the Activity has already been paused 666 */ 667 public void disableForegroundDispatch(Activity activity) { 668 ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, 669 mForegroundDispatchListener); 670 disableForegroundDispatchInternal(activity, false); 671 } 672 673 OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() { 674 @Override 675 public void onPaused(Activity activity) { 676 disableForegroundDispatchInternal(activity, true); 677 } 678 }; 679 680 void disableForegroundDispatchInternal(Activity activity, boolean force) { 681 try { 682 sService.setForegroundDispatch(null, null, null); 683 if (!force && !activity.isResumed()) { 684 throw new IllegalStateException("You must disable foreground dispatching " + 685 "while your activity is still resumed"); 686 } 687 } catch (RemoteException e) { 688 attemptDeadServiceRecovery(e); 689 } 690 } 691 692 /** 693 * Enable NDEF message push over NFC while this Activity is in the foreground. 694 * 695 * <p>You must explicitly call this method every time the activity is 696 * resumed, and you must call {@link #disableForegroundNdefPush} before 697 * your activity completes {@link Activity#onPause}. 698 * 699 * <p>Strongly recommend to use the new {@link #setNdefPushMessage} 700 * instead: it automatically hooks into your activity life-cycle, 701 * so you do not need to call enable/disable in your onResume/onPause. 702 * 703 * <p>For NDEF push to function properly the other NFC device must 704 * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or 705 * Android's "com.android.npp" (Ndef Push Protocol). This was optional 706 * on Gingerbread level Android NFC devices, but SNEP is mandatory on 707 * Ice-Cream-Sandwich and beyond. 708 * 709 * <p>This method must be called from the main thread. 710 * 711 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 712 * 713 * @param activity foreground activity 714 * @param message a NDEF Message to push over NFC 715 * @throws IllegalStateException if the activity is not currently in the foreground 716 * @deprecated use {@link #setNdefPushMessage} instead 717 */ 718 @Deprecated 719 public void enableForegroundNdefPush(Activity activity, NdefMessage message) { 720 if (activity == null || message == null) { 721 throw new NullPointerException(); 722 } 723 enforceResumed(activity); 724 mNfcActivityManager.setNdefPushMessage(activity, message); 725 } 726 727 /** 728 * Disable NDEF message push over P2P. 729 * 730 * <p>After calling {@link #enableForegroundNdefPush}, an activity 731 * must call this method before its {@link Activity#onPause} callback 732 * completes. 733 * 734 * <p>Strongly recommend to use the new {@link #setNdefPushMessage} 735 * instead: it automatically hooks into your activity life-cycle, 736 * so you do not need to call enable/disable in your onResume/onPause. 737 * 738 * <p>This method must be called from the main thread. 739 * 740 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 741 * 742 * @param activity the Foreground activity 743 * @throws IllegalStateException if the Activity has already been paused 744 * @deprecated use {@link #setNdefPushMessage} instead 745 */ 746 public void disableForegroundNdefPush(Activity activity) { 747 if (activity == null) { 748 throw new NullPointerException(); 749 } 750 enforceResumed(activity); 751 mNfcActivityManager.setNdefPushMessage(activity, null); 752 mNfcActivityManager.setNdefPushMessageCallback(activity, null); 753 mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null); 754 } 755 756 /** 757 * Enable NDEF Push feature. 758 * <p>This API is for the Settings application. 759 * @hide 760 */ 761 public boolean enableNdefPush() { 762 try { 763 return sService.enableNdefPush(); 764 } catch (RemoteException e) { 765 attemptDeadServiceRecovery(e); 766 return false; 767 } 768 } 769 770 /** 771 * Disable NDEF Push feature. 772 * <p>This API is for the Settings application. 773 * @hide 774 */ 775 public boolean disableNdefPush() { 776 try { 777 return sService.disableNdefPush(); 778 } catch (RemoteException e) { 779 attemptDeadServiceRecovery(e); 780 return false; 781 } 782 } 783 784 /** 785 * Return true if NDEF Push feature is enabled. 786 * <p>This function can return true even if NFC is currently turned-off. 787 * This indicates that NDEF Push is not currently active, but it has 788 * been requested by the user and will be active as soon as NFC is turned 789 * on. 790 * <p>If you want to check if NDEF PUsh sharing is currently active, use 791 * <code>{@link #isEnabled()} && {@link #isNdefPushEnabled()}</code> 792 * 793 * @return true if NDEF Push feature is enabled 794 * @hide 795 */ 796 public boolean isNdefPushEnabled() { 797 try { 798 return sService.isNdefPushEnabled(); 799 } catch (RemoteException e) { 800 attemptDeadServiceRecovery(e); 801 return false; 802 } 803 } 804 805 /** 806 * @hide 807 */ 808 public INfcAdapterExtras getNfcAdapterExtrasInterface() { 809 try { 810 return sService.getNfcAdapterExtrasInterface(); 811 } catch (RemoteException e) { 812 attemptDeadServiceRecovery(e); 813 return null; 814 } 815 } 816 817 void enforceResumed(Activity activity) { 818 if (!activity.isResumed()) { 819 throw new IllegalStateException("API cannot be called while activity is paused"); 820 } 821 } 822} 823