• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.nfc;
18 
19 import android.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.app.Activity;
22 import android.app.ActivityThread;
23 import android.app.OnActivityPausedListener;
24 import android.app.PendingIntent;
25 import android.content.Context;
26 import android.content.IntentFilter;
27 import android.content.pm.IPackageManager;
28 import android.content.pm.PackageManager;
29 import android.nfc.tech.MifareClassic;
30 import android.nfc.tech.Ndef;
31 import android.nfc.tech.NfcA;
32 import android.nfc.tech.NfcF;
33 import android.os.IBinder;
34 import android.os.RemoteException;
35 import android.os.ServiceManager;
36 import 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  */
44 public 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      *   &lt;activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"&gt;
74      *       &lt;!-- Add a technology filter --&gt;
75      *       &lt;intent-filter&gt;
76      *           &lt;action android:name="android.nfc.action.TECH_DISCOVERED" /&gt;
77      *       &lt;/intent-filter&gt;
78      *
79      *       &lt;meta-data android:name="android.nfc.action.TECH_DISCOVERED"
80      *           android:resource="@xml/filter_nfc"
81      *       /&gt;
82      *   &lt;/activity&gt;
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      * &lt;resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"&gt;
99      *     &lt;!-- capture anything using NfcF --&gt;
100      *     &lt;tech-list&gt;
101      *         &lt;tech&gt;android.nfc.tech.NfcF&lt;/tech&gt;
102      *     &lt;/tech-list&gt;
103      *
104      *     &lt;!-- OR --&gt;
105      *
106      *     &lt;!-- capture all MIFARE Classics with NDEF payloads --&gt;
107      *     &lt;tech-list&gt;
108      *         &lt;tech&gt;android.nfc.tech.NfcA&lt;/tech&gt;
109      *         &lt;tech&gt;android.nfc.tech.MifareClassic&lt;/tech&gt;
110      *         &lt;tech&gt;android.nfc.tech.Ndef&lt;/tech&gt;
111      *     &lt;/tech-list&gt;
112      * &lt;/resources&gt;
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     // Guarded by NfcAdapter.class
240     private static boolean sIsInitialized = false;
241 
242     // Final after first constructor, except for
243     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
244     // recovery
245     private static INfcAdapter sService;
246     private static INfcTag sTagService;
247 
248     /**
249      * Helper to check if this device has FEATURE_NFC, but without using
250      * a context.
251      * Equivalent to
252      * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
253      */
hasNfcFeature()254     private static boolean hasNfcFeature() {
255         IPackageManager pm = ActivityThread.getPackageManager();
256         if (pm == null) {
257             Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
258             return false;
259         }
260         try {
261             return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
262         } catch (RemoteException e) {
263             Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
264             return false;
265         }
266     }
267 
setupService()268     private static synchronized INfcAdapter setupService() {
269         if (!sIsInitialized) {
270             sIsInitialized = true;
271 
272             /* is this device meant to have NFC */
273             if (!hasNfcFeature()) {
274                 Log.v(TAG, "this device does not have NFC support");
275                 return null;
276             }
277 
278             sService = getServiceInterface();
279             if (sService == null) {
280                 Log.e(TAG, "could not retrieve NFC service");
281                 return null;
282             }
283             try {
284                 sTagService = sService.getNfcTagInterface();
285             } catch (RemoteException e) {
286                 Log.e(TAG, "could not retrieve NFC Tag service");
287                 return null;
288             }
289         }
290         return sService;
291     }
292 
293     /** get handle to NFC service interface */
getServiceInterface()294     private static INfcAdapter getServiceInterface() {
295         /* get a handle to NFC service */
296         IBinder b = ServiceManager.getService("nfc");
297         if (b == null) {
298             return null;
299         }
300         return INfcAdapter.Stub.asInterface(b);
301     }
302 
303     /**
304      * Helper to get the default NFC Adapter.
305      * <p>
306      * Most Android devices will only have one NFC Adapter (NFC Controller).
307      * <p>
308      * This helper is the equivalent of:
309      * <pre>{@code
310      * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
311      * NfcAdapter adapter = manager.getDefaultAdapter();
312      * }</pre>
313      * @param context the calling application's context
314      *
315      * @return the default NFC adapter, or null if no NFC adapter exists
316      */
getDefaultAdapter(Context context)317     public static NfcAdapter getDefaultAdapter(Context context) {
318         /* use getSystemService() instead of just instantiating to take
319          * advantage of the context's cached NfcManager & NfcAdapter */
320         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
321         return manager.getDefaultAdapter();
322     }
323 
324     /**
325      * Get a handle to the default NFC Adapter on this Android device.
326      * <p>
327      * Most Android devices will only have one NFC Adapter (NFC Controller).
328      *
329      * @return the default NFC adapter, or null if no NFC adapter exists
330      * @deprecated use {@link #getDefaultAdapter(Context)}
331      */
332     @Deprecated
getDefaultAdapter()333     public static NfcAdapter getDefaultAdapter() {
334         Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
335                 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
336         return new NfcAdapter(null);
337     }
338 
NfcAdapter(Context context)339     /*package*/ NfcAdapter(Context context) {
340         if (setupService() == null) {
341             throw new UnsupportedOperationException();
342         }
343     }
344 
345     /**
346      * Returns the binder interface to the service.
347      * @hide
348      */
getService()349     public INfcAdapter getService() {
350         isEnabled();  // NOP call to recover sService if it is stale
351         return sService;
352     }
353 
354     /**
355      * Returns the binder interface to the tag service.
356      * @hide
357      */
getTagService()358     public INfcTag getTagService() {
359         isEnabled();  // NOP call to recover sTagService if it is stale
360         return sTagService;
361     }
362 
363     /**
364      * NFC service dead - attempt best effort recovery
365      * @hide
366      */
attemptDeadServiceRecovery(Exception e)367     public void attemptDeadServiceRecovery(Exception e) {
368         Log.e(TAG, "NFC service dead - attempting to recover", e);
369         INfcAdapter service = getServiceInterface();
370         if (service == null) {
371             Log.e(TAG, "could not retrieve NFC service during service recovery");
372             // nothing more can be done now, sService is still stale, we'll hit
373             // this recovery path again later
374             return;
375         }
376         // assigning to sService is not thread-safe, but this is best-effort code
377         // and on a well-behaved system should never happen
378         sService = service;
379         try {
380             sTagService = service.getNfcTagInterface();
381         } catch (RemoteException ee) {
382             Log.e(TAG, "could not retrieve NFC tag service during service recovery");
383             // nothing more can be done now, sService is still stale, we'll hit
384             // this recovery path again later
385         }
386 
387         return;
388     }
389 
390     /**
391      * Return true if this NFC Adapter has any features enabled.
392      *
393      * <p>Application may use this as a helper to suggest that the user
394      * should turn on NFC in Settings.
395      * <p>If this method returns false, the NFC hardware is guaranteed not to
396      * generate or respond to any NFC transactions.
397      *
398      * @return true if this NFC Adapter has any features enabled
399      */
isEnabled()400     public boolean isEnabled() {
401         try {
402             return sService.isEnabled();
403         } catch (RemoteException e) {
404             attemptDeadServiceRecovery(e);
405             return false;
406         }
407     }
408 
409     /**
410      * Enable NFC hardware.
411      * <p>
412      * NOTE: may block for ~second or more.  Poor API.  Avoid
413      * calling from the UI thread.
414      *
415      * @hide
416      */
enable()417     public boolean enable() {
418         try {
419             return sService.enable();
420         } catch (RemoteException e) {
421             attemptDeadServiceRecovery(e);
422             return false;
423         }
424     }
425 
426     /**
427      * Disable NFC hardware.
428      * No NFC features will work after this call, and the hardware
429      * will not perform or respond to any NFC communication.
430      * <p>
431      * NOTE: may block for ~second or more.  Poor API.  Avoid
432      * calling from the UI thread.
433      *
434      * @hide
435      */
disable()436     public boolean disable() {
437         try {
438             return sService.disable();
439         } catch (RemoteException e) {
440             attemptDeadServiceRecovery(e);
441             return false;
442         }
443     }
444 
445     /**
446      * Enable foreground dispatch to the given Activity.
447      *
448      * <p>This will give give priority to the foreground activity when
449      * dispatching a discovered {@link Tag} to an application.
450      *
451      * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents
452      * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and
453      * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED}
454      * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled
455      * by passing in the tech lists separately. Each first level entry in the tech list represents
456      * an array of technologies that must all be present to match. If any of the first level sets
457      * match then the dispatch is routed through the given PendingIntent. In other words, the second
458      * level is ANDed together and the first level entries are ORed together.
459      *
460      * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters
461      * that acts a wild card and will cause the foreground activity to receive all tags via the
462      * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent.
463      *
464      * <p>This method must be called from the main thread, and only when the activity is in the
465      * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before
466      * the completion of their {@link Activity#onPause} callback to disable foreground dispatch
467      * after it has been enabled.
468      *
469      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
470      *
471      * @param activity the Activity to dispatch to
472      * @param intent the PendingIntent to start for the dispatch
473      * @param filters the IntentFilters to override dispatching for, or null to always dispatch
474      * @param techLists the tech lists used to perform matching for dispatching of the
475      *      {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent
476      * @throws IllegalStateException if the Activity is not currently in the foreground
477      */
enableForegroundDispatch(Activity activity, PendingIntent intent, IntentFilter[] filters, String[][] techLists)478     public void enableForegroundDispatch(Activity activity, PendingIntent intent,
479             IntentFilter[] filters, String[][] techLists) {
480         if (activity == null || intent == null) {
481             throw new NullPointerException();
482         }
483         if (!activity.isResumed()) {
484             throw new IllegalStateException("Foregorund dispatching can only be enabled " +
485                     "when your activity is resumed");
486         }
487         try {
488             TechListParcel parcel = null;
489             if (techLists != null && techLists.length > 0) {
490                 parcel = new TechListParcel(techLists);
491             }
492             ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
493                     mForegroundDispatchListener);
494             sService.enableForegroundDispatch(activity.getComponentName(), intent, filters,
495                     parcel);
496         } catch (RemoteException e) {
497             attemptDeadServiceRecovery(e);
498         }
499     }
500 
501     /**
502      * Disable foreground dispatch to the given activity.
503      *
504      * <p>After calling {@link #enableForegroundDispatch}, an activity
505      * must call this method before its {@link Activity#onPause} callback
506      * completes.
507      *
508      * <p>This method must be called from the main thread.
509      *
510      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
511      *
512      * @param activity the Activity to disable dispatch to
513      * @throws IllegalStateException if the Activity has already been paused
514      */
disableForegroundDispatch(Activity activity)515     public void disableForegroundDispatch(Activity activity) {
516         ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
517                 mForegroundDispatchListener);
518         disableForegroundDispatchInternal(activity, false);
519     }
520 
521     OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() {
522         @Override
523         public void onPaused(Activity activity) {
524             disableForegroundDispatchInternal(activity, true);
525         }
526     };
527 
disableForegroundDispatchInternal(Activity activity, boolean force)528     void disableForegroundDispatchInternal(Activity activity, boolean force) {
529         try {
530             sService.disableForegroundDispatch(activity.getComponentName());
531             if (!force && !activity.isResumed()) {
532                 throw new IllegalStateException("You must disable forgeground dispatching " +
533                         "while your activity is still resumed");
534             }
535         } catch (RemoteException e) {
536             attemptDeadServiceRecovery(e);
537         }
538     }
539 
540     /**
541      * Enable NDEF message push over P2P while this Activity is in the foreground.
542      *
543      * <p>For this to function properly the other NFC device being scanned must
544      * support the "com.android.npp" NDEF push protocol. Support for this
545      * protocol is currently optional for Android NFC devices.
546      *
547      * <p>This method must be called from the main thread.
548      *
549      * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled.
550      * Only the foreground activity may receive tag discovered dispatches via
551      * {@link #enableForegroundDispatch}.
552      *
553      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
554      *
555      * @param activity the foreground Activity
556      * @param msg a NDEF Message to push over P2P
557      * @throws IllegalStateException if the Activity is not currently in the foreground
558      * @throws OperationNotSupportedException if this Android device does not support NDEF push
559      */
enableForegroundNdefPush(Activity activity, NdefMessage msg)560     public void enableForegroundNdefPush(Activity activity, NdefMessage msg) {
561         if (activity == null || msg == null) {
562             throw new NullPointerException();
563         }
564         if (!activity.isResumed()) {
565             throw new IllegalStateException("Foregorund NDEF push can only be enabled " +
566                     "when your activity is resumed");
567         }
568         try {
569             ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
570                     mForegroundNdefPushListener);
571             sService.enableForegroundNdefPush(activity.getComponentName(), msg);
572         } catch (RemoteException e) {
573             attemptDeadServiceRecovery(e);
574         }
575     }
576 
577     /**
578      * Disable NDEF message push over P2P.
579      *
580      * <p>After calling {@link #enableForegroundNdefPush}, an activity
581      * must call this method before its {@link Activity#onPause} callback
582      * completes.
583      *
584      * <p>This method must be called from the main thread.
585      *
586      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
587      *
588      * @param activity the Foreground activity
589      * @throws IllegalStateException if the Activity has already been paused
590      * @throws OperationNotSupportedException if this Android device does not support NDEF push
591      */
disableForegroundNdefPush(Activity activity)592     public void disableForegroundNdefPush(Activity activity) {
593         ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
594                 mForegroundNdefPushListener);
595         disableForegroundNdefPushInternal(activity, false);
596     }
597 
598     OnActivityPausedListener mForegroundNdefPushListener = new OnActivityPausedListener() {
599         @Override
600         public void onPaused(Activity activity) {
601             disableForegroundNdefPushInternal(activity, true);
602         }
603     };
604 
disableForegroundNdefPushInternal(Activity activity, boolean force)605     void disableForegroundNdefPushInternal(Activity activity, boolean force) {
606         try {
607             sService.disableForegroundNdefPush(activity.getComponentName());
608             if (!force && !activity.isResumed()) {
609                 throw new IllegalStateException("You must disable forgeground NDEF push " +
610                         "while your activity is still resumed");
611             }
612         } catch (RemoteException e) {
613             attemptDeadServiceRecovery(e);
614         }
615     }
616 
617     /**
618      * Set the NDEF Message that this NFC adapter should appear as to Tag
619      * readers.
620      * <p>
621      * Any Tag reader can read the contents of the local tag when it is in
622      * proximity, without any further user confirmation.
623      * <p>
624      * The implementation of this method must either
625      * <ul>
626      * <li>act as a passive tag containing this NDEF message
627      * <li>provide the NDEF message on over LLCP to peer NFC adapters
628      * </ul>
629      * The NDEF message is preserved across reboot.
630      * <p>Requires {@link android.Manifest.permission#NFC} permission.
631      *
632      * @param message NDEF message to make public
633      * @hide
634      */
setLocalNdefMessage(NdefMessage message)635     public void setLocalNdefMessage(NdefMessage message) {
636         try {
637             sService.localSet(message);
638         } catch (RemoteException e) {
639             attemptDeadServiceRecovery(e);
640         }
641     }
642 
643     /**
644      * Get the NDEF Message that this adapter appears as to Tag readers.
645      * <p>Requires {@link android.Manifest.permission#NFC} permission.
646      *
647      * @return NDEF Message that is publicly readable
648      * @hide
649      */
getLocalNdefMessage()650     public NdefMessage getLocalNdefMessage() {
651         try {
652             return sService.localGet();
653         } catch (RemoteException e) {
654             attemptDeadServiceRecovery(e);
655             return null;
656         }
657     }
658 
659     /**
660      * @hide
661      */
getNfcAdapterExtrasInterface()662     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
663         try {
664             return sService.getNfcAdapterExtrasInterface();
665         } catch (RemoteException e) {
666             attemptDeadServiceRecovery(e);
667             return null;
668         }
669     }
670 }
671