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