NfcAdapter.java revision a77258b98ec1aa7f605a93e4be59c3540c90c05c
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 private 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: an adapter's state changed between enabled and disabled. 161 * 162 * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains 163 * whether it's enabled or disabled, not including any information about whether it's 164 * actively enabling or disabling. 165 * 166 * @hide 167 */ 168 public static final String ACTION_ADAPTER_STATE_CHANGE = 169 "android.nfc.action.ADAPTER_STATE_CHANGE"; 170 171 /** 172 * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is. 173 * 174 * @hide 175 */ 176 public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled"; 177 178 /** 179 * LLCP link status: The LLCP link is activated. 180 * @hide 181 */ 182 public static final int LLCP_LINK_STATE_ACTIVATED = 0; 183 184 /** 185 * LLCP link status: The LLCP link is deactivated. 186 * @hide 187 */ 188 public static final int LLCP_LINK_STATE_DEACTIVATED = 1; 189 190 /** 191 * Broadcast Action: the LLCP link state changed. 192 * <p> 193 * Always contains the extra field 194 * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}. 195 * @hide 196 */ 197 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 198 public static final String ACTION_LLCP_LINK_STATE_CHANGED = 199 "android.nfc.action.LLCP_LINK_STATE_CHANGED"; 200 201 /** 202 * Used as int extra field in 203 * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}. 204 * <p> 205 * It contains the new state of the LLCP link. 206 * @hide 207 */ 208 public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE"; 209 210 /** 211 * Tag Reader Discovery mode 212 * @hide 213 */ 214 private static final int DISCOVERY_MODE_TAG_READER = 0; 215 216 /** 217 * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an 218 * NFC-IP1 communication. Implementations should not assume that the 219 * controller will end up behaving as an NFC-IP1 target or initiator and 220 * should handle both cases, depending on the type of the remote peer type. 221 * @hide 222 */ 223 private static final int DISCOVERY_MODE_NFCIP1 = 1; 224 225 /** 226 * Card Emulation mode Enables the manager to act as an NFC tag. Provided 227 * that a Secure Element (an UICC for instance) is connected to the NFC 228 * controller through its SWP interface, it can be exposed to the outside 229 * NFC world and be addressed by external readers the same way they would 230 * with a tag. 231 * <p> 232 * Which Secure Element is exposed is implementation-dependent. 233 * 234 * @hide 235 */ 236 private static final int DISCOVERY_MODE_CARD_EMULATION = 2; 237 238 /** 239 * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This 240 */ 241 public interface NdefPushCallback { 242 /** 243 * Called when a P2P connection is created. 244 */ 245 NdefMessage createMessage(); 246 /** 247 * Called when the message is pushed. 248 */ 249 void onMessagePushed(); 250 } 251 252 private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub { 253 private NdefPushCallback mCallback; 254 255 public NdefPushCallbackWrapper(NdefPushCallback callback) { 256 mCallback = callback; 257 } 258 259 @Override 260 public NdefMessage onConnect() { 261 return mCallback.createMessage(); 262 } 263 264 @Override 265 public void onMessagePushed() { 266 mCallback.onMessagePushed(); 267 } 268 } 269 270 // Guarded by NfcAdapter.class 271 private static boolean sIsInitialized = false; 272 273 // Final after first constructor, except for 274 // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort 275 // recovery 276 private static INfcAdapter sService; 277 private static INfcTag sTagService; 278 279 /** 280 * Helper to check if this device has FEATURE_NFC, but without using 281 * a context. 282 * Equivalent to 283 * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC) 284 */ 285 private static boolean hasNfcFeature() { 286 IPackageManager pm = ActivityThread.getPackageManager(); 287 if (pm == null) { 288 Log.e(TAG, "Cannot get package manager, assuming no NFC feature"); 289 return false; 290 } 291 try { 292 return pm.hasSystemFeature(PackageManager.FEATURE_NFC); 293 } catch (RemoteException e) { 294 Log.e(TAG, "Package manager query failed, assuming no NFC feature", e); 295 return false; 296 } 297 } 298 299 private static synchronized INfcAdapter setupService() { 300 if (!sIsInitialized) { 301 sIsInitialized = true; 302 303 /* is this device meant to have NFC */ 304 if (!hasNfcFeature()) { 305 Log.v(TAG, "this device does not have NFC support"); 306 return null; 307 } 308 309 sService = getServiceInterface(); 310 if (sService == null) { 311 Log.e(TAG, "could not retrieve NFC service"); 312 return null; 313 } 314 try { 315 sTagService = sService.getNfcTagInterface(); 316 } catch (RemoteException e) { 317 Log.e(TAG, "could not retrieve NFC Tag service"); 318 return null; 319 } 320 } 321 return sService; 322 } 323 324 /** get handle to NFC service interface */ 325 private static INfcAdapter getServiceInterface() { 326 /* get a handle to NFC service */ 327 IBinder b = ServiceManager.getService("nfc"); 328 if (b == null) { 329 return null; 330 } 331 return INfcAdapter.Stub.asInterface(b); 332 } 333 334 /** 335 * Helper to get the default NFC Adapter. 336 * <p> 337 * Most Android devices will only have one NFC Adapter (NFC Controller). 338 * <p> 339 * This helper is the equivalent of: 340 * <pre>{@code 341 * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 342 * NfcAdapter adapter = manager.getDefaultAdapter(); 343 * }</pre> 344 * @param context the calling application's context 345 * 346 * @return the default NFC adapter, or null if no NFC adapter exists 347 */ 348 public static NfcAdapter getDefaultAdapter(Context context) { 349 /* use getSystemService() instead of just instantiating to take 350 * advantage of the context's cached NfcManager & NfcAdapter */ 351 NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE); 352 return manager.getDefaultAdapter(); 353 } 354 355 /** 356 * Get a handle to the default NFC Adapter on this Android device. 357 * <p> 358 * Most Android devices will only have one NFC Adapter (NFC Controller). 359 * 360 * @return the default NFC adapter, or null if no NFC adapter exists 361 * @deprecated use {@link #getDefaultAdapter(Context)} 362 */ 363 @Deprecated 364 public static NfcAdapter getDefaultAdapter() { 365 Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + 366 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); 367 return new NfcAdapter(null); 368 } 369 370 /*package*/ NfcAdapter(Context context) { 371 if (setupService() == null) { 372 throw new UnsupportedOperationException(); 373 } 374 } 375 376 /** 377 * Returns the binder interface to the service. 378 * @hide 379 */ 380 public INfcAdapter getService() { 381 isEnabled(); // NOP call to recover sService if it is stale 382 return sService; 383 } 384 385 /** 386 * Returns the binder interface to the tag service. 387 * @hide 388 */ 389 public INfcTag getTagService() { 390 isEnabled(); // NOP call to recover sTagService if it is stale 391 return sTagService; 392 } 393 394 /** 395 * NFC service dead - attempt best effort recovery 396 * @hide 397 */ 398 public void attemptDeadServiceRecovery(Exception e) { 399 Log.e(TAG, "NFC service dead - attempting to recover", e); 400 INfcAdapter service = getServiceInterface(); 401 if (service == null) { 402 Log.e(TAG, "could not retrieve NFC service during service recovery"); 403 // nothing more can be done now, sService is still stale, we'll hit 404 // this recovery path again later 405 return; 406 } 407 // assigning to sService is not thread-safe, but this is best-effort code 408 // and on a well-behaved system should never happen 409 sService = service; 410 try { 411 sTagService = service.getNfcTagInterface(); 412 } catch (RemoteException ee) { 413 Log.e(TAG, "could not retrieve NFC tag service during service recovery"); 414 // nothing more can be done now, sService is still stale, we'll hit 415 // this recovery path again later 416 } 417 418 return; 419 } 420 421 /** 422 * Return true if this NFC Adapter has any features enabled. 423 * 424 * <p>Application may use this as a helper to suggest that the user 425 * should turn on NFC in Settings. 426 * <p>If this method returns false, the NFC hardware is guaranteed not to 427 * generate or respond to any NFC transactions. 428 * 429 * @return true if this NFC Adapter has any features enabled 430 */ 431 public boolean isEnabled() { 432 try { 433 return sService.isEnabled(); 434 } catch (RemoteException e) { 435 attemptDeadServiceRecovery(e); 436 return false; 437 } 438 } 439 440 /** 441 * Enable NFC hardware. 442 * <p> 443 * NOTE: may block for ~second or more. Poor API. Avoid 444 * calling from the UI thread. 445 * 446 * @hide 447 */ 448 public boolean enable() { 449 try { 450 return sService.enable(); 451 } catch (RemoteException e) { 452 attemptDeadServiceRecovery(e); 453 return false; 454 } 455 } 456 457 /** 458 * Disable NFC hardware. 459 * No NFC features will work after this call, and the hardware 460 * will not perform or respond to any NFC communication. 461 * <p> 462 * NOTE: may block for ~second or more. Poor API. Avoid 463 * calling from the UI thread. 464 * 465 * @hide 466 */ 467 public boolean disable() { 468 try { 469 return sService.disable(); 470 } catch (RemoteException e) { 471 attemptDeadServiceRecovery(e); 472 return false; 473 } 474 } 475 476 /** 477 * Enable foreground dispatch to the given Activity. 478 * 479 * <p>This will give give priority to the foreground activity when 480 * dispatching a discovered {@link Tag} to an application. 481 * 482 * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents 483 * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and 484 * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED} 485 * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled 486 * by passing in the tech lists separately. Each first level entry in the tech list represents 487 * an array of technologies that must all be present to match. If any of the first level sets 488 * match then the dispatch is routed through the given PendingIntent. In other words, the second 489 * level is ANDed together and the first level entries are ORed together. 490 * 491 * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters 492 * that acts a wild card and will cause the foreground activity to receive all tags via the 493 * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent. 494 * 495 * <p>This method must be called from the main thread, and only when the activity is in the 496 * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before 497 * the completion of their {@link Activity#onPause} callback to disable foreground dispatch 498 * after it has been enabled. 499 * 500 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 501 * 502 * @param activity the Activity to dispatch to 503 * @param intent the PendingIntent to start for the dispatch 504 * @param filters the IntentFilters to override dispatching for, or null to always dispatch 505 * @param techLists the tech lists used to perform matching for dispatching of the 506 * {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent 507 * @throws IllegalStateException if the Activity is not currently in the foreground 508 */ 509 public void enableForegroundDispatch(Activity activity, PendingIntent intent, 510 IntentFilter[] filters, String[][] techLists) { 511 if (activity == null || intent == null) { 512 throw new NullPointerException(); 513 } 514 if (!activity.isResumed()) { 515 throw new IllegalStateException("Foregorund dispatching can only be enabled " + 516 "when your activity is resumed"); 517 } 518 try { 519 TechListParcel parcel = null; 520 if (techLists != null && techLists.length > 0) { 521 parcel = new TechListParcel(techLists); 522 } 523 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 524 mForegroundDispatchListener); 525 sService.enableForegroundDispatch(activity.getComponentName(), intent, filters, 526 parcel); 527 } catch (RemoteException e) { 528 attemptDeadServiceRecovery(e); 529 } 530 } 531 532 /** 533 * Disable foreground dispatch to the given activity. 534 * 535 * <p>After calling {@link #enableForegroundDispatch}, an activity 536 * must call this method before its {@link Activity#onPause} callback 537 * completes. 538 * 539 * <p>This method must be called from the main thread. 540 * 541 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 542 * 543 * @param activity the Activity to disable dispatch to 544 * @throws IllegalStateException if the Activity has already been paused 545 */ 546 public void disableForegroundDispatch(Activity activity) { 547 ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, 548 mForegroundDispatchListener); 549 disableForegroundDispatchInternal(activity, false); 550 } 551 552 OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() { 553 @Override 554 public void onPaused(Activity activity) { 555 disableForegroundDispatchInternal(activity, true); 556 } 557 }; 558 559 void disableForegroundDispatchInternal(Activity activity, boolean force) { 560 try { 561 sService.disableForegroundDispatch(activity.getComponentName()); 562 if (!force && !activity.isResumed()) { 563 throw new IllegalStateException("You must disable forgeground dispatching " + 564 "while your activity is still resumed"); 565 } 566 } catch (RemoteException e) { 567 attemptDeadServiceRecovery(e); 568 } 569 } 570 571 /** 572 * Enable NDEF message push over P2P while this Activity is in the foreground. 573 * 574 * <p>For this to function properly the other NFC device being scanned must 575 * support the "com.android.npp" NDEF push protocol. Support for this 576 * protocol is currently optional for Android NFC devices. 577 * 578 * <p>This method must be called from the main thread. 579 * 580 * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled. 581 * Only the foreground activity may receive tag discovered dispatches via 582 * {@link #enableForegroundDispatch}. 583 * 584 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 585 * 586 * @param activity the foreground Activity 587 * @param msg a NDEF Message to push over P2P 588 * @throws IllegalStateException if the Activity is not currently in the foreground 589 * @throws OperationNotSupportedException if this Android device does not support NDEF push 590 */ 591 public void enableForegroundNdefPush(Activity activity, NdefMessage msg) { 592 if (activity == null || msg == null) { 593 throw new NullPointerException(); 594 } 595 if (!activity.isResumed()) { 596 throw new IllegalStateException("Foregorund NDEF push can only be enabled " + 597 "when your activity is resumed"); 598 } 599 try { 600 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 601 mForegroundNdefPushListener); 602 sService.enableForegroundNdefPush(activity.getComponentName(), msg); 603 } catch (RemoteException e) { 604 attemptDeadServiceRecovery(e); 605 } 606 } 607 608 /** 609 * Enable NDEF message push over P2P while this Activity is in the foreground. 610 * 611 * <p>For this to function properly the other NFC device being scanned must 612 * support the "com.android.npp" NDEF push protocol. Support for this 613 * protocol is currently optional for Android NFC devices. 614 * 615 * <p>This method must be called from the main thread. 616 * 617 * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled. 618 * Only the foreground activity may receive tag discovered dispatches via 619 * {@link #enableForegroundDispatch}. 620 * 621 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 622 * 623 * @param activity the foreground Activity 624 * @param callback is called on when the P2P connection is established 625 * @throws IllegalStateException if the Activity is not currently in the foreground 626 * @throws OperationNotSupportedException if this Android device does not support NDEF push 627 */ 628 public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) { 629 if (activity == null || callback == null) { 630 throw new NullPointerException(); 631 } 632 if (!activity.isResumed()) { 633 throw new IllegalStateException("Foregorund NDEF push can only be enabled " + 634 "when your activity is resumed"); 635 } 636 try { 637 ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity, 638 mForegroundNdefPushListener); 639 sService.enableForegroundNdefPushWithCallback(activity.getComponentName(), 640 new NdefPushCallbackWrapper(callback)); 641 } catch (RemoteException e) { 642 attemptDeadServiceRecovery(e); 643 } 644 } 645 646 /** 647 * Disable NDEF message push over P2P. 648 * 649 * <p>After calling {@link #enableForegroundNdefPush}, an activity 650 * must call this method before its {@link Activity#onPause} callback 651 * completes. 652 * 653 * <p>This method must be called from the main thread. 654 * 655 * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission. 656 * 657 * @param activity the Foreground activity 658 * @throws IllegalStateException if the Activity has already been paused 659 * @throws OperationNotSupportedException if this Android device does not support NDEF push 660 */ 661 public void disableForegroundNdefPush(Activity activity) { 662 ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity, 663 mForegroundNdefPushListener); 664 disableForegroundNdefPushInternal(activity, false); 665 } 666 667 OnActivityPausedListener mForegroundNdefPushListener = new OnActivityPausedListener() { 668 @Override 669 public void onPaused(Activity activity) { 670 disableForegroundNdefPushInternal(activity, true); 671 } 672 }; 673 674 void disableForegroundNdefPushInternal(Activity activity, boolean force) { 675 try { 676 sService.disableForegroundNdefPush(activity.getComponentName()); 677 if (!force && !activity.isResumed()) { 678 throw new IllegalStateException("You must disable forgeground NDEF push " + 679 "while your activity is still resumed"); 680 } 681 } catch (RemoteException e) { 682 attemptDeadServiceRecovery(e); 683 } 684 } 685 686 /** 687 * @hide 688 */ 689 public INfcAdapterExtras getNfcAdapterExtrasInterface() { 690 try { 691 return sService.getNfcAdapterExtrasInterface(); 692 } catch (RemoteException e) { 693 attemptDeadServiceRecovery(e); 694 return null; 695 } 696 } 697} 698