Home | History | Annotate | Download | only in nfc
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project Licensed under the Apache
      3  * License, Version 2.0 (the "License"); you may not use this file except in
      4  * compliance with the License. You may obtain a copy of the License at
      5  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
      6  * or agreed to in writing, software distributed under the License is
      7  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
      8  * KIND, either express or implied. See the License for the specific language
      9  * governing permissions and limitations under the License.
     10  */
     11 
     12 package android.nfc;
     13 
     14 import java.lang.UnsupportedOperationException;
     15 
     16 import android.annotation.SdkConstant;
     17 import android.annotation.SdkConstant.SdkConstantType;
     18 import android.app.ActivityThread;
     19 import android.content.Context;
     20 import android.content.pm.IPackageManager;
     21 import android.content.pm.PackageManager;
     22 import android.nfc.INfcAdapter;
     23 import android.os.IBinder;
     24 import android.os.RemoteException;
     25 import android.os.ServiceManager;
     26 import android.util.Log;
     27 
     28 /**
     29  * Represents the device's local NFC adapter.
     30  * <p>
     31  * Use the static {@link #getDefaultAdapter} method to get the default NFC
     32  * Adapter for this Android device. Most Android devices will have only one NFC
     33  * Adapter, and {@link #getDefaultAdapter} returns the singleton object.
     34  */
     35 public final class NfcAdapter {
     36     /**
     37      * Intent to start an activity when a tag is discovered.
     38      */
     39     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     40     public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
     41 
     42     /**
     43      * Mandatory Tag extra for the ACTION_TAG intents.
     44      * @hide
     45      */
     46     public static final String EXTRA_TAG = "android.nfc.extra.TAG";
     47 
     48     /**
     49      * Optional NdefMessage[] extra for the ACTION_TAG intents.
     50      */
     51     public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     52 
     53     /**
     54      * Optional byte[] extra for the tag identifier.
     55      */
     56     public static final String EXTRA_ID = "android.nfc.extra.ID";
     57 
     58     /**
     59      * Broadcast Action: a transaction with a secure element has been detected.
     60      * <p>
     61      * Always contains the extra field
     62      * {@link android.nfc.NfcAdapter#EXTRA_AID}
     63      * @hide
     64      */
     65     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     66     public static final String ACTION_TRANSACTION_DETECTED =
     67             "android.nfc.action.TRANSACTION_DETECTED";
     68 
     69     /**
     70      * Broadcast Action: an adapter's state changed between enabled and disabled.
     71      *
     72      * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains
     73      * whether it's enabled or disabled, not including any information about whether it's
     74      * actively enabling or disabling.
     75      *
     76      * @hide
     77      */
     78     public static final String ACTION_ADAPTER_STATE_CHANGE =
     79             "android.nfc.action.ADAPTER_STATE_CHANGE";
     80 
     81     /**
     82      * The Intent extra for ACTION_ADAPTER_STATE_CHANGE, saying what the new state is.
     83      *
     84      * @hide
     85      */
     86     public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled";
     87 
     88     /**
     89      * Mandatory byte array extra field in
     90      * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}.
     91      * <p>
     92      * Contains the AID of the applet involved in the transaction.
     93      * @hide
     94      */
     95     public static final String EXTRA_AID = "android.nfc.extra.AID";
     96 
     97     /**
     98      * LLCP link status: The LLCP link is activated.
     99      * @hide
    100      */
    101     public static final int LLCP_LINK_STATE_ACTIVATED = 0;
    102 
    103     /**
    104      * LLCP link status: The LLCP link is deactivated.
    105      * @hide
    106      */
    107     public static final int LLCP_LINK_STATE_DEACTIVATED = 1;
    108 
    109     /**
    110      * Broadcast Action: the LLCP link state changed.
    111      * <p>
    112      * Always contains the extra field
    113      * {@link android.nfc.NfcAdapter#EXTRA_LLCP_LINK_STATE_CHANGED}.
    114      * @hide
    115      */
    116     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    117     public static final String ACTION_LLCP_LINK_STATE_CHANGED =
    118             "android.nfc.action.LLCP_LINK_STATE_CHANGED";
    119 
    120     /**
    121      * Used as int extra field in
    122      * {@link android.nfc.NfcAdapter#ACTION_LLCP_LINK_STATE_CHANGED}.
    123      * <p>
    124      * It contains the new state of the LLCP link.
    125      * @hide
    126      */
    127     public static final String EXTRA_LLCP_LINK_STATE_CHANGED = "android.nfc.extra.LLCP_LINK_STATE";
    128 
    129     /**
    130      * Tag Reader Discovery mode
    131      * @hide
    132      */
    133     private static final int DISCOVERY_MODE_TAG_READER = 0;
    134 
    135     /**
    136      * NFC-IP1 Peer-to-Peer mode Enables the manager to act as a peer in an
    137      * NFC-IP1 communication. Implementations should not assume that the
    138      * controller will end up behaving as an NFC-IP1 target or initiator and
    139      * should handle both cases, depending on the type of the remote peer type.
    140      * @hide
    141      */
    142     private static final int DISCOVERY_MODE_NFCIP1 = 1;
    143 
    144     /**
    145      * Card Emulation mode Enables the manager to act as an NFC tag. Provided
    146      * that a Secure Element (an UICC for instance) is connected to the NFC
    147      * controller through its SWP interface, it can be exposed to the outside
    148      * NFC world and be addressed by external readers the same way they would
    149      * with a tag.
    150      * <p>
    151      * Which Secure Element is exposed is implementation-dependent.
    152      *
    153      * @hide
    154      */
    155     private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
    156 
    157     private static final String TAG = "NFC";
    158 
    159     // Both guarded by NfcAdapter.class:
    160     private static boolean sIsInitialized = false;
    161     private static NfcAdapter sAdapter;
    162 
    163     // Final after construction, except for attemptDeadServiceRecovery()
    164     // when NFC crashes.
    165     // Not locked - we accept a best effort attempt when NFC crashes.
    166     /*package*/ INfcAdapter mService;
    167 
    168     private NfcAdapter(INfcAdapter service) {
    169         mService = service;
    170     }
    171 
    172     /**
    173      * Helper to check if this device has FEATURE_NFC, but without using
    174      * a context.
    175      * Equivalent to
    176      * context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)
    177      */
    178     private static boolean hasNfcFeature() {
    179         IPackageManager pm = ActivityThread.getPackageManager();
    180         if (pm == null) {
    181             Log.e(TAG, "Cannot get package manager, assuming no NFC feature");
    182             return false;
    183         }
    184         try {
    185             return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
    186         } catch (RemoteException e) {
    187             Log.e(TAG, "Package manager query failed, assuming no NFC feature", e);
    188             return false;
    189         }
    190     }
    191 
    192     /** get handle to NFC service interface */
    193     private static synchronized INfcAdapter getServiceInterface() {
    194         /* get a handle to NFC service */
    195         IBinder b = ServiceManager.getService("nfc");
    196         if (b == null) {
    197             return null;
    198         }
    199         return INfcAdapter.Stub.asInterface(b);
    200     }
    201 
    202     /**
    203      * Get a handle to the default NFC Adapter on this Android device.
    204      * <p>
    205      * Most Android devices will only have one NFC Adapter (NFC Controller).
    206      *
    207      * @return the default NFC adapter, or null if no NFC adapter exists
    208      */
    209     public static NfcAdapter getDefaultAdapter() {
    210         synchronized (NfcAdapter.class) {
    211             if (sIsInitialized) {
    212                 return sAdapter;
    213             }
    214             sIsInitialized = true;
    215 
    216             /* is this device meant to have NFC */
    217             if (!hasNfcFeature()) {
    218                 Log.v(TAG, "this device does not have NFC support");
    219                 return null;
    220             }
    221 
    222             INfcAdapter service = getServiceInterface();
    223             if (service == null) {
    224                 Log.e(TAG, "could not retrieve NFC service");
    225                 return null;
    226             }
    227 
    228             sAdapter = new NfcAdapter(service);
    229             return sAdapter;
    230         }
    231     }
    232 
    233     /** NFC service dead - attempt best effort recovery */
    234     /*package*/ void attemptDeadServiceRecovery(Exception e) {
    235         Log.e(TAG, "NFC service dead - attempting to recover", e);
    236         INfcAdapter service = getServiceInterface();
    237         if (service == null) {
    238             Log.e(TAG, "could not retrieve NFC service during service recovery");
    239             return;
    240         }
    241         /* assigning to mService is not thread-safe, but this is best-effort code
    242          * and on a well-behaved system should never happen */
    243         mService = service;
    244         return;
    245     }
    246 
    247     /**
    248      * Return true if this NFC Adapter has any features enabled.
    249      * <p>
    250      * If this method returns false, then applications should request the user
    251      * turn on NFC tag discovery in Settings.
    252      * <p>
    253      * If this method returns false, the NFC hardware is guaranteed not to
    254      * perform or respond to any NFC communication.
    255      *
    256      * @return true if this NFC Adapter is enabled to discover new tags
    257      */
    258     public boolean isEnabled() {
    259         try {
    260             return mService.isEnabled();
    261         } catch (RemoteException e) {
    262             attemptDeadServiceRecovery(e);
    263             return false;
    264         }
    265     }
    266 
    267     /**
    268      * Enable NFC hardware.
    269      * <p>
    270      * NOTE: may block for ~second or more.  Poor API.  Avoid
    271      * calling from the UI thread.
    272      *
    273      * @hide
    274      */
    275     public boolean enable() {
    276         try {
    277             return mService.enable();
    278         } catch (RemoteException e) {
    279             attemptDeadServiceRecovery(e);
    280             return false;
    281         }
    282     }
    283 
    284     /**
    285      * Disable NFC hardware.
    286      * No NFC features will work after this call, and the hardware
    287      * will not perform or respond to any NFC communication.
    288      * <p>
    289      * NOTE: may block for ~second or more.  Poor API.  Avoid
    290      * calling from the UI thread.
    291      *
    292      * @hide
    293      */
    294     public boolean disable() {
    295         try {
    296             return mService.disable();
    297         } catch (RemoteException e) {
    298             attemptDeadServiceRecovery(e);
    299             return false;
    300         }
    301     }
    302 
    303     /**
    304      * Create a raw tag connection to the default Target
    305      * <p>Requires {@link android.Manifest.permission#NFC} permission.
    306      * @hide
    307      */
    308     public RawTagConnection createRawTagConnection(Tag tag) {
    309         if (tag.mServiceHandle == 0) {
    310             throw new IllegalArgumentException("mock tag cannot be used for connections");
    311         }
    312         try {
    313             return new RawTagConnection(this, tag);
    314         } catch (RemoteException e) {
    315             attemptDeadServiceRecovery(e);
    316             return null;
    317         }
    318     }
    319 
    320     /**
    321      * Create a raw tag connection to the specified Target
    322      * <p>Requires {@link android.Manifest.permission#NFC} permission.
    323      * @hide
    324      */
    325     public RawTagConnection createRawTagConnection(Tag tag, String target) {
    326         if (tag.mServiceHandle == 0) {
    327             throw new IllegalArgumentException("mock tag cannot be used for connections");
    328         }
    329         try {
    330             return new RawTagConnection(this, tag, target);
    331         } catch (RemoteException e) {
    332             attemptDeadServiceRecovery(e);
    333             return null;
    334         }
    335     }
    336 
    337     /**
    338      * Create an NDEF tag connection to the default Target
    339      * <p>Requires {@link android.Manifest.permission#NFC} permission.
    340      * @hide
    341      */
    342     public NdefTagConnection createNdefTagConnection(NdefTag tag) {
    343         if (tag.mServiceHandle == 0) {
    344             throw new IllegalArgumentException("mock tag cannot be used for connections");
    345         }
    346         try {
    347             return new NdefTagConnection(this, tag);
    348         } catch (RemoteException e) {
    349             attemptDeadServiceRecovery(e);
    350             return null;
    351         }
    352     }
    353 
    354     /**
    355      * Create an NDEF tag connection to the specified Target
    356      * <p>Requires {@link android.Manifest.permission#NFC} permission.
    357      * @hide
    358      */
    359     public NdefTagConnection createNdefTagConnection(NdefTag tag, String target) {
    360         if (tag.mServiceHandle == 0) {
    361             throw new IllegalArgumentException("mock tag cannot be used for connections");
    362         }
    363         try {
    364             return new NdefTagConnection(this, tag, target);
    365         } catch (RemoteException e) {
    366             attemptDeadServiceRecovery(e);
    367             return null;
    368         }
    369     }
    370 }
    371