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