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