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