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.SdkConstant;
     20 import android.annotation.SdkConstant.SdkConstantType;
     21 import android.app.Activity;
     22 import android.app.ActivityThread;
     23 import android.app.OnActivityPausedListener;
     24 import android.app.PendingIntent;
     25 import android.content.Context;
     26 import android.content.IntentFilter;
     27 import android.content.pm.IPackageManager;
     28 import android.content.pm.PackageManager;
     29 import android.nfc.tech.MifareClassic;
     30 import android.nfc.tech.Ndef;
     31 import android.nfc.tech.NfcA;
     32 import android.nfc.tech.NfcF;
     33 import android.os.IBinder;
     34 import android.os.RemoteException;
     35 import android.os.ServiceManager;
     36 import android.util.Log;
     37 
     38 /**
     39  * Represents the local NFC adapter.
     40  * <p>
     41  * Use the helper {@link #getDefaultAdapter(Context)} to get the default NFC
     42  * adapter for this Android device.
     43  */
     44 public final class NfcAdapter {
     45     static final String TAG = "NFC";
     46 
     47     /**
     48      * Intent to start an activity when a tag with NDEF payload is discovered.
     49      *
     50      * <p>The system inspects the first {@link NdefRecord} in the first {@link NdefMessage} and
     51      * looks for a URI, SmartPoster, or MIME record. If a URI or SmartPoster record is found the
     52      * intent will contain the URI in its data field. If a MIME record is found the intent will
     53      * contain the MIME type in its type field. This allows activities to register
     54      * {@link IntentFilter}s targeting specific content on tags. Activities should register the
     55      * most specific intent filters possible to avoid the activity chooser dialog, which can
     56      * disrupt the interaction with the tag as the user interacts with the screen.
     57      *
     58      * <p>If the tag has an NDEF payload this intent is started before
     59      * {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither
     60      * {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started.
     61      */
     62     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     63     public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     64 
     65     /**
     66      * Intent to start an activity when a tag is discovered and activities are registered for the
     67      * specific technologies on the tag.
     68      *
     69      * <p>To receive this intent an activity must include an intent filter
     70      * for this action and specify the desired tech types in a
     71      * manifest <code>meta-data</code> entry. Here is an example manfiest entry:
     72      * <pre>
     73      *   &lt;activity android:name=".nfc.TechFilter" android:label="NFC/TechFilter"&gt;
     74      *       &lt;!-- Add a technology filter --&gt;
     75      *       &lt;intent-filter&gt;
     76      *           &lt;action android:name="android.nfc.action.TECH_DISCOVERED" /&gt;
     77      *       &lt;/intent-filter&gt;
     78      *
     79      *       &lt;meta-data android:name="android.nfc.action.TECH_DISCOVERED"
     80      *           android:resource="@xml/filter_nfc"
     81      *       /&gt;
     82      *   &lt;/activity&gt;
     83      * </pre>
     84      *
     85      * <p>The meta-data XML file should contain one or more <code>tech-list</code> entries
     86      * each consisting or one or more <code>tech</code> entries. The <code>tech</code> entries refer
     87      * to the qualified class name implementing the technology, for example "android.nfc.tech.NfcA".
     88      *
     89      * <p>A tag matches if any of the
     90      * <code>tech-list</code> sets is a subset of {@link Tag#getTechList() Tag.getTechList()}. Each
     91      * of the <code>tech-list</code>s is considered independently and the
     92      * activity is considered a match is any single <code>tech-list</code> matches the tag that was
     93      * discovered. This provides AND and OR semantics for filtering desired techs. Here is an
     94      * example that will match any tag using {@link NfcF} or any tag using {@link NfcA},
     95      * {@link MifareClassic}, and {@link Ndef}:
     96      *
     97      * <pre>
     98      * &lt;resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"&gt;
     99      *     &lt;!-- capture anything using NfcF --&gt;
    100      *     &lt;tech-list&gt;
    101      *         &lt;tech&gt;android.nfc.tech.NfcF&lt;/tech&gt;
    102      *     &lt;/tech-list&gt;
    103      *
    104      *     &lt;!-- OR --&gt;
    105      *
    106      *     &lt;!-- capture all MIFARE Classics with NDEF payloads --&gt;
    107      *     &lt;tech-list&gt;
    108      *         &lt;tech&gt;android.nfc.tech.NfcA&lt;/tech&gt;
    109      *         &lt;tech&gt;android.nfc.tech.MifareClassic&lt;/tech&gt;
    110      *         &lt;tech&gt;android.nfc.tech.Ndef&lt;/tech&gt;
    111      *     &lt;/tech-list&gt;
    112      * &lt;/resources&gt;
    113      * </pre>
    114      *
    115      * <p>This intent is started after {@link #ACTION_NDEF_DISCOVERED} and before
    116      * {@link #ACTION_TAG_DISCOVERED}. If any activities respond to {@link #ACTION_NDEF_DISCOVERED}
    117      * this intent will not be started. If any activities respond to this intent
    118      * {@link #ACTION_TAG_DISCOVERED} will not be started.
    119      */
    120     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    121     public static final String ACTION_TECH_DISCOVERED = "android.nfc.action.TECH_DISCOVERED";
    122 
    123     /**
    124      * Intent to start an activity when a tag is discovered.
    125      *
    126      * <p>This intent will not be started when a tag is discovered if any activities respond to
    127      * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag.
    128      */
    129     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    130     public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
    131 
    132     /**
    133      * Broadcast to only the activity that handles ACTION_TAG_DISCOVERED
    134      * @hide
    135      */
    136     public static final String ACTION_TAG_LEFT_FIELD = "android.nfc.action.TAG_LOST";
    137 
    138     /**
    139      * Mandatory extra containing the {@link Tag} that was discovered for the
    140      * {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
    141      * {@link #ACTION_TAG_DISCOVERED} intents.
    142      */
    143     public static final String EXTRA_TAG = "android.nfc.extra.TAG";
    144 
    145     /**
    146      * Optional extra containing an array of {@link NdefMessage} present on the discovered tag for
    147      * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
    148      * {@link #ACTION_TAG_DISCOVERED} intents.
    149      */
    150     public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
    151 
    152     /**
    153      * Optional extra containing a byte array containing the ID of the discovered tag for
    154      * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
    155      * {@link #ACTION_TAG_DISCOVERED} intents.
    156      */
    157     public static final String EXTRA_ID = "android.nfc.extra.ID";
    158 
    159     /**
    160      * Broadcast Action: The state of the local NFC adapter has been
    161      * changed.
    162      * <p>For example, NFC has been turned on or off.
    163      * <p>Always contains the extra field {@link #EXTRA_STATE}
    164      * @hide
    165      */
    166     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    167     public static final String ACTION_ADAPTER_STATE_CHANGED =
    168             "android.nfc.action.ADAPTER_STATE_CHANGED";
    169 
    170     /**
    171      * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
    172      * intents to request the current power state. Possible values are:
    173      * {@link #STATE_OFF},
    174      * {@link #STATE_TURNING_ON},
    175      * {@link #STATE_ON},
    176      * {@link #STATE_TURNING_OFF},
    177      * @hide
    178      */
    179     public static final String EXTRA_ADAPTER_STATE = "android.nfc.extra.ADAPTER_STATE";
    180 
    181     /** @hide */
    182     public static final int STATE_OFF = 1;
    183     /** @hide */
    184     public static final int STATE_TURNING_ON = 2;
    185     /** @hide */
    186     public static final int STATE_ON = 3;
    187     /** @hide */
    188     public static final int STATE_TURNING_OFF = 4;
    189 
    190     // Guarded by NfcAdapter.class
    191     static boolean sIsInitialized = false;
    192 
    193     // Final after first constructor, except for
    194     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
    195     // recovery
    196     static INfcAdapter sService;
    197     static INfcTag sTagService;
    198 
    199     /**
    200      * NfcAdapter is currently a singleton, and does not require a context.
    201      * However all the public API's are future-proofed to require a context.
    202      * If we start using that then we'll need to keep a HashMap of
    203      * Context.getApplicationContext() -> NfcAdapter, such that NfcAdapter
    204      * is a singleton within each application context.
    205      */
    206     static NfcAdapter sSingleton;  // protected by NfcAdapter.class
    207 
    208     final NfcActivityManager mNfcActivityManager;
    209 
    210     /**
    211      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
    212      * to another device.
    213      * @see #setOnNdefPushCompleteCallback
    214      */
    215     public interface OnNdefPushCompleteCallback {
    216         /**
    217          * Called on successful NDEF push.
    218          *
    219          * <p>This callback is usually made on a binder thread (not the UI thread).
    220          *
    221          * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
    222          * @see #setNdefPushMessageCallback
    223          */
    224         public void onNdefPushComplete(NfcEvent event);
    225     }
    226 
    227     /**
    228      * A callback to be invoked when another NFC device capable of NDEF push (Android Beam)
    229      * is within range.
    230      * <p>Implement this interface and pass it to {@link
    231      * NfcAdapter#setNdefPushMessageCallback setNdefPushMessageCallback()} in order to create an
    232      * {@link NdefMessage} at the moment that another device is within range for NFC. Using this
    233      * callback allows you to create a message with data that might vary based on the
    234      * content currently visible to the user. Alternatively, you can call {@link
    235      * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
    236      * same data.
    237      */
    238     public interface CreateNdefMessageCallback {
    239         /**
    240          * Called to provide a {@link NdefMessage} to push.
    241          *
    242          * <p>This callback is usually made on a binder thread (not the UI thread).
    243          *
    244          * <p>Called when this device is in range of another device
    245          * that might support NDEF push. It allows the application to
    246          * create the NDEF message only when it is required.
    247          *
    248          * <p>NDEF push cannot occur until this method returns, so do not
    249          * block for too long.
    250          *
    251          * <p>The Android operating system will usually show a system UI
    252          * on top of your activity during this time, so do not try to request
    253          * input from the user to complete the callback, or provide custom NDEF
    254          * push UI. The user probably will not see it.
    255          *
    256          * @param event {@link NfcEvent} with the {@link NfcEvent#nfcAdapter} field set
    257          * @return NDEF message to push, or null to not provide a message
    258          */
    259         public NdefMessage createNdefMessage(NfcEvent event);
    260     }
    261 
    262     /**
    263      * Helper to check if this device has FEATURE_NFC, but without using
    264      * a context.
    265      * Equivalent to
    266      * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
    267      */
    268     private static boolean hasNfcFeature() {
    269         IPackageManager pm = ActivityThread.getPackageManager();
    270         if (pm == null) {
    271             Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
    272             return false;
    273         }
    274         try {
    275             return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
    276         } catch (RemoteException e) {
    277             Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
    278             return false;
    279         }
    280     }
    281 
    282     /**
    283      * Returns the singleton, or throws if NFC is not available.
    284      */
    285     static synchronized NfcAdapter getSingleton() {
    286         if (!sIsInitialized) {
    287             sIsInitialized = true;
    288 
    289             /* is this device meant to have NFC */
    290             if (!hasNfcFeature()) {
    291                 Log.v(TAG, "this device does not have NFC support");
    292                 throw new UnsupportedOperationException();
    293             }
    294 
    295             sService = getServiceInterface();
    296             if (sService == null) {
    297                 Log.e(TAG, "could not retrieve NFC service");
    298                 throw new UnsupportedOperationException();
    299             }
    300             try {
    301                 sTagService = sService.getNfcTagInterface();
    302             } catch (RemoteException e) {
    303                 Log.e(TAG, "could not retrieve NFC Tag service");
    304                 throw new UnsupportedOperationException();
    305             }
    306             sSingleton = new NfcAdapter();
    307         }
    308         if (sSingleton == null) {
    309             throw new UnsupportedOperationException();
    310         }
    311         return sSingleton;
    312     }
    313 
    314     /** get handle to NFC service interface */
    315     private static INfcAdapter getServiceInterface() {
    316         /* get a handle to NFC service */
    317         IBinder b = ServiceManager.getService("nfc");
    318         if (b == null) {
    319             return null;
    320         }
    321         return INfcAdapter.Stub.asInterface(b);
    322     }
    323 
    324     /**
    325      * Helper to get the default NFC Adapter.
    326      * <p>
    327      * Most Android devices will only have one NFC Adapter (NFC Controller).
    328      * <p>
    329      * This helper is the equivalent of:
    330      * <pre>{@code
    331      * NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
    332      * NfcAdapter adapter = manager.getDefaultAdapter();
    333      * }</pre>
    334      * @param context the calling application's context
    335      *
    336      * @return the default NFC adapter, or null if no NFC adapter exists
    337      */
    338     public static NfcAdapter getDefaultAdapter(Context context) {
    339         /* use getSystemService() instead of just instantiating to take
    340          * advantage of the context's cached NfcManager & NfcAdapter */
    341         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
    342         return manager.getDefaultAdapter();
    343     }
    344 
    345     /**
    346      * Get a handle to the default NFC Adapter on this Android device.
    347      * <p>
    348      * Most Android devices will only have one NFC Adapter (NFC Controller).
    349      *
    350      * @return the default NFC adapter, or null if no NFC adapter exists
    351      * @deprecated use {@link #getDefaultAdapter(Context)}
    352      */
    353     @Deprecated
    354     public static NfcAdapter getDefaultAdapter() {
    355         Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
    356                 "NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
    357         return getSingleton();
    358     }
    359 
    360     /**
    361      * Does not currently need a context.
    362      */
    363     NfcAdapter() {
    364         mNfcActivityManager = new NfcActivityManager(this);
    365     }
    366 
    367     /**
    368      * Returns the binder interface to the service.
    369      * @hide
    370      */
    371     public INfcAdapter getService() {
    372         isEnabled();  // NOP call to recover sService if it is stale
    373         return sService;
    374     }
    375 
    376     /**
    377      * Returns the binder interface to the tag service.
    378      * @hide
    379      */
    380     public INfcTag getTagService() {
    381         isEnabled();  // NOP call to recover sTagService if it is stale
    382         return sTagService;
    383     }
    384 
    385     /**
    386      * NFC service dead - attempt best effort recovery
    387      * @hide
    388      */
    389     public void attemptDeadServiceRecovery(Exception e) {
    390         Log.e(TAG, "NFC service dead - attempting to recover", e);
    391         INfcAdapter service = getServiceInterface();
    392         if (service == null) {
    393             Log.e(TAG, "could not retrieve NFC service during service recovery");
    394             // nothing more can be done now, sService is still stale, we'll hit
    395             // this recovery path again later
    396             return;
    397         }
    398         // assigning to sService is not thread-safe, but this is best-effort code
    399         // and on a well-behaved system should never happen
    400         sService = service;
    401         try {
    402             sTagService = service.getNfcTagInterface();
    403         } catch (RemoteException ee) {
    404             Log.e(TAG, "could not retrieve NFC tag service during service recovery");
    405             // nothing more can be done now, sService is still stale, we'll hit
    406             // this recovery path again later
    407         }
    408 
    409         return;
    410     }
    411 
    412     /**
    413      * Return true if this NFC Adapter has any features enabled.
    414      *
    415      * <p>Application may use this as a helper to suggest that the user
    416      * should turn on NFC in Settings.
    417      * <p>If this method returns false, the NFC hardware is guaranteed not to
    418      * generate or respond to any NFC transactions.
    419      *
    420      * @return true if this NFC Adapter has any features enabled
    421      */
    422     public boolean isEnabled() {
    423         try {
    424             return sService.getState() == STATE_ON;
    425         } catch (RemoteException e) {
    426             attemptDeadServiceRecovery(e);
    427             return false;
    428         }
    429     }
    430 
    431     /**
    432      * Return the state of this NFC Adapter.
    433      *
    434      * <p>Returns one of {@link #STATE_ON}, {@link #STATE_TURNING_ON},
    435      * {@link #STATE_OFF}, {@link #STATE_TURNING_OFF}.
    436      *
    437      * <p>{@link #isEnabled()} is equivalent to
    438      * <code>{@link #getAdapterState()} == {@link #STATE_ON}</code>
    439      *
    440      * @return the current state of this NFC adapter
    441      *
    442      * @hide
    443      */
    444     public int getAdapterState() {
    445         try {
    446             return sService.getState();
    447         } catch (RemoteException e) {
    448             attemptDeadServiceRecovery(e);
    449             return NfcAdapter.STATE_OFF;
    450         }
    451     }
    452 
    453     /**
    454      * Enable NFC hardware.
    455      *
    456      * <p>This call is asynchronous. Listen for
    457      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
    458      * operation is complete.
    459      *
    460      * <p>If this returns true, then either NFC is already on, or
    461      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
    462      * to indicate a state transition. If this returns false, then
    463      * there is some problem that prevents an attempt to turn
    464      * NFC on (for example we are in airplane mode and NFC is not
    465      * toggleable in airplane mode on this platform).
    466      *
    467      * @hide
    468      */
    469     public boolean enable() {
    470         try {
    471             return sService.enable();
    472         } catch (RemoteException e) {
    473             attemptDeadServiceRecovery(e);
    474             return false;
    475         }
    476     }
    477 
    478     /**
    479      * Disable NFC hardware.
    480      *
    481      * <p>No NFC features will work after this call, and the hardware
    482      * will not perform or respond to any NFC communication.
    483      *
    484      * <p>This call is asynchronous. Listen for
    485      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
    486      * operation is complete.
    487      *
    488      * <p>If this returns true, then either NFC is already off, or
    489      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
    490      * to indicate a state transition. If this returns false, then
    491      * there is some problem that prevents an attempt to turn
    492      * NFC off.
    493      *
    494      * @hide
    495      */
    496     public boolean disable() {
    497         try {
    498             return sService.disable();
    499         } catch (RemoteException e) {
    500             attemptDeadServiceRecovery(e);
    501             return false;
    502         }
    503     }
    504 
    505     /**
    506      * Set the {@link NdefMessage} to push over NFC during the specified activities.
    507      *
    508      * <p>This method may be called at any time, but the NDEF message is
    509      * only made available for NDEF push when one of the specified activities
    510      * is in resumed (foreground) state.
    511      *
    512      * <p>Only one NDEF message can be pushed by the currently resumed activity.
    513      * If both {@link #setNdefPushMessage} and
    514      * {@link #setNdefPushMessageCallback} are set then
    515      * the callback will take priority.
    516      *
    517      * <p>Pass a null NDEF message to disable foreground NDEF push in the
    518      * specified activities.
    519      *
    520      * <p>At least one activity must be specified, and usually only one is necessary.
    521      *
    522      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    523      *
    524      * @param message NDEF message to push over NFC, or null to disable
    525      * @param activity an activity in which NDEF push should be enabled to share the provided
    526      *                 NDEF message
    527      * @param activities optional additional activities that should also enable NDEF push with
    528      *                   the provided NDEF message
    529      */
    530     public void setNdefPushMessage(NdefMessage message, Activity activity,
    531             Activity ... activities) {
    532         if (activity == null) {
    533             throw new NullPointerException("activity cannot be null");
    534         }
    535         mNfcActivityManager.setNdefPushMessage(activity, message);
    536         for (Activity a : activities) {
    537             if (a == null) {
    538                 throw new NullPointerException("activities cannot contain null");
    539             }
    540             mNfcActivityManager.setNdefPushMessage(a, message);
    541         }
    542     }
    543 
    544     /**
    545      * Set the callback to create a {@link NdefMessage} to push over NFC.
    546      *
    547      * <p>This method may be called at any time, but this callback is
    548      * only made if one of the specified activities
    549      * is in resumed (foreground) state.
    550      *
    551      * <p>Only one NDEF message can be pushed by the currently resumed activity.
    552      * If both {@link #setNdefPushMessage} and
    553      * {@link #setNdefPushMessageCallback} are set then
    554      * the callback will take priority.
    555      *
    556      * <p>Pass a null callback to disable the callback in the
    557      * specified activities.
    558      *
    559      * <p>At least one activity must be specified, and usually only one is necessary.
    560      *
    561      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    562      *
    563      * @param callback callback, or null to disable
    564      * @param activity an activity in which NDEF push should be enabled to share an NDEF message
    565      *                 that's retrieved from the provided callback
    566      * @param activities optional additional activities that should also enable NDEF push using
    567      *                   the provided callback
    568      */
    569     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
    570             Activity ... activities) {
    571         if (activity == null) {
    572             throw new NullPointerException("activity cannot be null");
    573         }
    574         mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
    575         for (Activity a : activities) {
    576             if (a == null) {
    577                 throw new NullPointerException("activities cannot contain null");
    578             }
    579             mNfcActivityManager.setNdefPushMessageCallback(a, callback);
    580         }
    581     }
    582 
    583     /**
    584      * Set the callback on a successful NDEF push over NFC.
    585      *
    586      * <p>This method may be called at any time, but NDEF push and this callback
    587      * can only occur when one of the specified activities is in resumed
    588      * (foreground) state.
    589      *
    590      * <p>One or more activities must be specified.
    591      *
    592      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    593      *
    594      * @param callback callback, or null to disable
    595      * @param activity an activity to enable the callback (at least one is required)
    596      * @param activities zero or more additional activities to enable to callback
    597      */
    598     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
    599             Activity activity, Activity ... activities) {
    600         if (activity == null) {
    601             throw new NullPointerException("activity cannot be null");
    602         }
    603         mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
    604         for (Activity a : activities) {
    605             if (a == null) {
    606                 throw new NullPointerException("activities cannot contain null");
    607             }
    608             mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
    609         }
    610     }
    611 
    612     /**
    613      * Enable foreground dispatch to the given Activity.
    614      *
    615      * <p>This will give give priority to the foreground activity when
    616      * dispatching a discovered {@link Tag} to an application.
    617      *
    618      * <p>If any IntentFilters are provided to this method they are used to match dispatch Intents
    619      * for both the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} and
    620      * {@link NfcAdapter#ACTION_TAG_DISCOVERED}. Since {@link NfcAdapter#ACTION_TECH_DISCOVERED}
    621      * relies on meta data outside of the IntentFilter matching for that dispatch Intent is handled
    622      * by passing in the tech lists separately. Each first level entry in the tech list represents
    623      * an array of technologies that must all be present to match. If any of the first level sets
    624      * match then the dispatch is routed through the given PendingIntent. In other words, the second
    625      * level is ANDed together and the first level entries are ORed together.
    626      *
    627      * <p>If you pass {@code null} for both the {@code filters} and {@code techLists} parameters
    628      * that acts a wild card and will cause the foreground activity to receive all tags via the
    629      * {@link NfcAdapter#ACTION_TAG_DISCOVERED} intent.
    630      *
    631      * <p>This method must be called from the main thread, and only when the activity is in the
    632      * foreground (resumed). Also, activities must call {@link #disableForegroundDispatch} before
    633      * the completion of their {@link Activity#onPause} callback to disable foreground dispatch
    634      * after it has been enabled.
    635      *
    636      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    637      *
    638      * @param activity the Activity to dispatch to
    639      * @param intent the PendingIntent to start for the dispatch
    640      * @param filters the IntentFilters to override dispatching for, or null to always dispatch
    641      * @param techLists the tech lists used to perform matching for dispatching of the
    642      *      {@link NfcAdapter#ACTION_TECH_DISCOVERED} intent
    643      * @throws IllegalStateException if the Activity is not currently in the foreground
    644      */
    645     public void enableForegroundDispatch(Activity activity, PendingIntent intent,
    646             IntentFilter[] filters, String[][] techLists) {
    647         if (activity == null || intent == null) {
    648             throw new NullPointerException();
    649         }
    650         if (!activity.isResumed()) {
    651             throw new IllegalStateException("Foreground dispatch can only be enabled " +
    652                     "when your activity is resumed");
    653         }
    654         try {
    655             TechListParcel parcel = null;
    656             if (techLists != null && techLists.length > 0) {
    657                 parcel = new TechListParcel(techLists);
    658             }
    659             ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
    660                     mForegroundDispatchListener);
    661             sService.setForegroundDispatch(intent, filters, parcel);
    662         } catch (RemoteException e) {
    663             attemptDeadServiceRecovery(e);
    664         }
    665     }
    666 
    667     /**
    668      * Disable foreground dispatch to the given activity.
    669      *
    670      * <p>After calling {@link #enableForegroundDispatch}, an activity
    671      * must call this method before its {@link Activity#onPause} callback
    672      * completes.
    673      *
    674      * <p>This method must be called from the main thread.
    675      *
    676      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    677      *
    678      * @param activity the Activity to disable dispatch to
    679      * @throws IllegalStateException if the Activity has already been paused
    680      */
    681     public void disableForegroundDispatch(Activity activity) {
    682         ActivityThread.currentActivityThread().unregisterOnActivityPausedListener(activity,
    683                 mForegroundDispatchListener);
    684         disableForegroundDispatchInternal(activity, false);
    685     }
    686 
    687     OnActivityPausedListener mForegroundDispatchListener = new OnActivityPausedListener() {
    688         @Override
    689         public void onPaused(Activity activity) {
    690             disableForegroundDispatchInternal(activity, true);
    691         }
    692     };
    693 
    694     void disableForegroundDispatchInternal(Activity activity, boolean force) {
    695         try {
    696             sService.setForegroundDispatch(null, null, null);
    697             if (!force && !activity.isResumed()) {
    698                 throw new IllegalStateException("You must disable foreground dispatching " +
    699                         "while your activity is still resumed");
    700             }
    701         } catch (RemoteException e) {
    702             attemptDeadServiceRecovery(e);
    703         }
    704     }
    705 
    706     /**
    707      * Enable NDEF message push over NFC while this Activity is in the foreground.
    708      *
    709      * <p>You must explicitly call this method every time the activity is
    710      * resumed, and you must call {@link #disableForegroundNdefPush} before
    711      * your activity completes {@link Activity#onPause}.
    712      *
    713      * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
    714      * instead: it automatically hooks into your activity life-cycle,
    715      * so you do not need to call enable/disable in your onResume/onPause.
    716      *
    717      * <p>For NDEF push to function properly the other NFC device must
    718      * support either NFC Forum's SNEP (Simple Ndef Exchange Protocol), or
    719      * Android's "com.android.npp" (Ndef Push Protocol). This was optional
    720      * on Gingerbread level Android NFC devices, but SNEP is mandatory on
    721      * Ice-Cream-Sandwich and beyond.
    722      *
    723      * <p>This method must be called from the main thread.
    724      *
    725      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    726      *
    727      * @param activity foreground activity
    728      * @param message a NDEF Message to push over NFC
    729      * @throws IllegalStateException if the activity is not currently in the foreground
    730      * @deprecated use {@link #setNdefPushMessage} instead
    731      */
    732     @Deprecated
    733     public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
    734         if (activity == null || message == null) {
    735             throw new NullPointerException();
    736         }
    737         enforceResumed(activity);
    738         mNfcActivityManager.setNdefPushMessage(activity, message);
    739     }
    740 
    741     /**
    742      * Disable NDEF message push over P2P.
    743      *
    744      * <p>After calling {@link #enableForegroundNdefPush}, an activity
    745      * must call this method before its {@link Activity#onPause} callback
    746      * completes.
    747      *
    748      * <p>Strongly recommend to use the new {@link #setNdefPushMessage}
    749      * instead: it automatically hooks into your activity life-cycle,
    750      * so you do not need to call enable/disable in your onResume/onPause.
    751      *
    752      * <p>This method must be called from the main thread.
    753      *
    754      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
    755      *
    756      * @param activity the Foreground activity
    757      * @throws IllegalStateException if the Activity has already been paused
    758      * @deprecated use {@link #setNdefPushMessage} instead
    759      */
    760     public void disableForegroundNdefPush(Activity activity) {
    761         if (activity == null) {
    762             throw new NullPointerException();
    763         }
    764         enforceResumed(activity);
    765         mNfcActivityManager.setNdefPushMessage(activity, null);
    766         mNfcActivityManager.setNdefPushMessageCallback(activity, null);
    767         mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
    768     }
    769 
    770     /**
    771      * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
    772      * @deprecated use {@link CreateNdefMessageCallback} or {@link OnNdefPushCompleteCallback}
    773      * @hide
    774      */
    775     @Deprecated
    776     public interface NdefPushCallback {
    777         /**
    778          * @deprecated use {@link CreateNdefMessageCallback} instead
    779          */
    780         @Deprecated
    781         NdefMessage createMessage();
    782         /**
    783          * @deprecated use{@link OnNdefPushCompleteCallback} instead
    784          */
    785         @Deprecated
    786         void onMessagePushed();
    787     }
    788 
    789     /**
    790      * TODO: Remove this
    791      * Converts new callbacks to old callbacks.
    792      */
    793     static final class LegacyCallbackWrapper implements CreateNdefMessageCallback,
    794             OnNdefPushCompleteCallback {
    795         final NdefPushCallback mLegacyCallback;
    796         LegacyCallbackWrapper(NdefPushCallback legacyCallback) {
    797             mLegacyCallback = legacyCallback;
    798         }
    799         @Override
    800         public void onNdefPushComplete(NfcEvent event) {
    801             mLegacyCallback.onMessagePushed();
    802         }
    803         @Override
    804         public NdefMessage createNdefMessage(NfcEvent event) {
    805             return mLegacyCallback.createMessage();
    806         }
    807     }
    808 
    809     /**
    810      * TODO: Remove this once pre-built apk's (Maps, Youtube etc) are updated
    811      * @deprecated use {@link #setNdefPushMessageCallback} instead
    812      * @hide
    813      */
    814     @Deprecated
    815     public void enableForegroundNdefPush(Activity activity, final NdefPushCallback callback) {
    816         if (activity == null || callback == null) {
    817             throw new NullPointerException();
    818         }
    819         enforceResumed(activity);
    820         LegacyCallbackWrapper callbackWrapper = new LegacyCallbackWrapper(callback);
    821         mNfcActivityManager.setNdefPushMessageCallback(activity, callbackWrapper);
    822         mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callbackWrapper);
    823     }
    824 
    825     /**
    826      * Enable NDEF Push feature.
    827      * <p>This API is for the Settings application.
    828      * @hide
    829      */
    830     public boolean enableNdefPush() {
    831         try {
    832             return sService.enableNdefPush();
    833         } catch (RemoteException e) {
    834             attemptDeadServiceRecovery(e);
    835             return false;
    836         }
    837     }
    838 
    839     /**
    840      * Disable NDEF Push feature.
    841      * <p>This API is for the Settings application.
    842      * @hide
    843      */
    844     public boolean disableNdefPush() {
    845         try {
    846             return sService.disableNdefPush();
    847         } catch (RemoteException e) {
    848             attemptDeadServiceRecovery(e);
    849             return false;
    850         }
    851     }
    852 
    853     /**
    854      * Return true if NDEF Push feature is enabled.
    855      * <p>This function can return true even if NFC is currently turned-off.
    856      * This indicates that NDEF Push is not currently active, but it has
    857      * been requested by the user and will be active as soon as NFC is turned
    858      * on.
    859      * <p>If you want to check if NDEF PUsh sharing is currently active, use
    860      * <code>{@link #isEnabled()} && {@link #isNdefPushEnabled()}</code>
    861      *
    862      * @return true if NDEF Push feature is enabled
    863      * @hide
    864      */
    865     public boolean isNdefPushEnabled() {
    866         try {
    867             return sService.isNdefPushEnabled();
    868         } catch (RemoteException e) {
    869             attemptDeadServiceRecovery(e);
    870             return false;
    871         }
    872     }
    873 
    874     /**
    875      * @hide
    876      */
    877     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
    878         try {
    879             return sService.getNfcAdapterExtrasInterface();
    880         } catch (RemoteException e) {
    881             attemptDeadServiceRecovery(e);
    882             return null;
    883         }
    884     }
    885 
    886     void enforceResumed(Activity activity) {
    887         if (!activity.isResumed()) {
    888             throw new IllegalStateException("API cannot be called while activity is paused");
    889         }
    890     }
    891 }
    892