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