This document describes the basic NFC tasks you perform in Android. It explains how to send and receive NFC data in the form of NDEF messages and describes the Android framework APIs that support these features.
There are two major uses cases when working with NDEF data and Android:
- Reading NDEF data from an NFC tag
- Beaming NDEF messages from one device to another withAndroid Beam™
Before you begin writing your NFC applications, it is important to understand the different types of NFC tags, how the tag dispatch system parses NFC tags, and the special work that the tag dispatch system does when it detects an NDEF message. NFC tags come in a wide array of technologies and can also have data written to them in many different ways.
The tag dispatch system defines three intents, which are listed in order of highest to lowest priority:
ACTION_NDEF_DISCOVERED
: This intent is used to start an Activity when a tag that contains an NDEF payload is scanned and is of a recognized type. This is the highest priority intent, and the tag dispatch system tries to start an Activity with this intent before any other intent, whenever possible.ACTION_TECH_DISCOVERED
: If no activities register to handle theACTION_NDEF_DISCOVERED
intent, the tag dispatch system tries to start an application with this intent. This intent is also directly started (without startingACTION_NDEF_DISCOVERED
first) if the tag that is scanned contains NDEF data that cannot be mapped to a MIME type or URI, or if the tag does not contain NDEF data but is of a known tag technology.ACTION_TAG_DISCOVERED
: This intent is started if no activities handle theACTION_NDEF_DISCOVERED
orACTION_TECH_DISCOVERED
intents.
- The minimum SDK version that your application can support. API level 9 only supports limited tag dispatch via
ACTION_TAG_DISCOVERED
, and only gives access to NDEF messages via theEXTRA_NDEF_MESSAGES
extra. No other tag properties or I/O operations are accessible. API level 10 includes comprehensive reader/writer support as well as foreground NDEF pushing, and API level 14 provides an easier way to push NDEF messages to other devices with Android Beam and extra convenience methods to create NDEF records.<uses-sdk android:minSdkVersion="10"/>
- The
uses-feature
element so that your application shows up in Google Play only for devices that have NFC hardware:<uses-feature android:name="android.hardware.nfc" android:required="true" />
> Filtering for NFC Intents
To start your application when an NFC tag that you want to handle is scanned, your application can filter for one, two, or all three of the NFC intents in the Android manifest. However, you usually want to filter for theACTION_NDEF_DISCOVERED
intent for the most control of when your application starts. TheACTION_TECH_DISCOVERED
intent is a fallback for ACTION_NDEF_DISCOVERED
when no applications filter forACTION_NDEF_DISCOVERED
or for when the payload is not NDEF. Filtering for ACTION_TAG_DISCOVERED
is usually too general of a category to filter on. Many applications will filter for ACTION_NDEF_DISCOVERED
orACTION_TECH_DISCOVERED
before ACTION_TAG_DISCOVERED
, so your application has a low probability of starting. ACTION_TAG_DISCOVERED
is only available as a last resort for applications to filter for in the cases where no other applications are installed to handle the ACTION_NDEF_DISCOVERED
orACTION_TECH_DISCOVERED
intent.
> The following example filters for ACTION_NDEF_DISCOVERED
intents with a MIME type oftext/plain
:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
The following example filters for a URI in the form of http://developer.android.com/index.html
.
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="http" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
>If your activity filters for the ACTION_TECH_DISCOVERED
intent, you must create an XML resource file that specifies the technologies that your activity supports within a tech-list
set. Your activity is considered a match if a tech-list
set is a subset of the technologies that are supported by the tag, which you can obtain by calling getTechList()
.
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.IsoDep</tech> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.NfcF</tech> <tech>android.nfc.tech.NfcV</tech> <tech>android.nfc.tech.Ndef</tech> <tech>android.nfc.tech.NdefFormatable</tech> <tech>android.nfc.tech.MifareClassic</tech> <tech>android.nfc.tech.MifareUltralight</tech> </tech-list> </resources>
In your AndroidManifest.xml
file, specify the resource file that you just created in the <meta-data>
element inside the <activity>
element like in the following example:
<activity> ... <intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED"/> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> ... </activity>》 The following example checks for the
ACTION_NDEF_DISCOVERED
intent and gets the NDEF messages from an intent extra.
public void onResume() { super.onResume(); ... if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMsgs != null) { msgs = new NdefMessage[rawMsgs.length]; for (int i = 0; i < rawMsgs.length; i++) { msgs[i] = (NdefMessage) rawMsgs[i]; } } } //process the msgs array }
>Note: We recommend that you use the RTD_URI
type instead of TNF_ABSOLUTE_URI
, because it is more efficient.
you can create a TNF_WELL_KNOWN
NDEF record in the following way:
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); byte[] textBytes = payload.getBytes(utfEncoding); int utfBit = encodeInUtf8 ? 0 : (1 << 7); char status = (char) (utfBit + langBytes.length); byte[] data = new byte[1 + langBytes.length + textBytes.length]; data[0] = (byte) status; System.arraycopy(langBytes, 0, data, 1, langBytes.length); System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; }
- The NFC device that is receiving the beamed data must support the
com.android.npp
NDEF push protocol or NFC Forum's SNEP (Simple NDEF Exchange Protocol). Thecom.android.npp
protocol is required for devices on API level 9 (Android 2.3) to API level 13 (Android 3.2).com.android.npp
and SNEP are both required on API level 14 (Android 4.0) and later.
NfcAdapter.CreateNdefMessageCallback
in the
onCreate()
method of an activity (see
AndroidBeamDemo
for the complete sample). This example also has methods to help you create a MIME record:
package com.example.android.beam; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcAdapter.CreateNdefMessageCallback; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; import java.nio.charset.Charset; public class Beam extends Activity implements CreateNdefMessageCallback { NfcAdapter mNfcAdapter; TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView textView = (TextView) findViewById(R.id.textView); // Check for available NFC Adapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show(); finish(); return; } // Register callback mNfcAdapter.setNdefPushMessageCallback(this, this); } @Override public NdefMessage createNdefMessage(NfcEvent event) { String text = ("Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( new NdefRecord[] { createMime( "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. */ //,NdefRecord.createApplicationRecord("com.example.android.beam") }); return msg; } @Override public void onResume() { super.onResume(); // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } } @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent setIntent(intent); } /** * Parses the NDEF Message from the intent and prints to the TextView */ void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.textView); Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); // only one message sent during the beam NdefMessage msg = (NdefMessage) rawMsgs[0]; // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); } }
> Note that this code comments out an AAR, which you can remove. If you enable the AAR, the application specified in the AAR always receives the Android Beam message. If the application is not present, Google Play is started to download the application. Therefore, the following intent filter is not technically necessary for Android 4.0 devices or later if the AAR is used:
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter>