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