Android NFC Application Development

Original author: Liang Zhang
  • Transfer

NFC (near field communication) is a standardized technology for exchanging data over short distances, allowing interactions between two electronic devices in a simple and intuitive way. For example, using an NFC-equipped smartphone, you can shop, give out business cards, download discount coupons, and so on. Many new applications for NFC will be found shortly.
This article describes the technologies that use NFC and how to use them today. It also shows how to use NFC in Android applications, and finally, there are two examples of NFC applications with source codes.

NFC Technology Architecture

NFC is based on RFID technology with a frequency of 13.56 MHz and a working distance of up to 10 cm. The data exchange rate is up to 424 kb / s. Compared to other communication technologies, the main advantage of NFC is its speed and ease of use. The figure below shows the location of NFC among other communication technologies.



NFC technology has three modes: NFC card emulation, peer-to-peer mode and read / write mode.



In emulation mode, the NFC card is an analog of a chip RFID card with its own security module, which allows you to protect the purchase process. In peer-to-peer mode, you can share information, such as a business card, with other NFC devices. You can also establish WiFi or Bluetooth connections via NFC to transfer large amounts of data. The read / write mode is intended for reading or changing NFC tags using NFC devices.
Each mode is described in more detail below.

NFC card emulation mode

An NFC module usually consists of two parts: an NFC controller and a security element (EB). The NFC controller is responsible for communication, the electronic module is responsible for encryption and decryption of sensitive information.



The EB is connected to the NFC controller via the SWP (Single Wire Protocol) bus or DCLB (Digital Contactless Bridge). NFC standards define the logical interface between the host and the controller, allowing them to communicate through the RF field. EB is implemented using an embedded application or OS component.



There are three options for implementing an EB: you can embed it in a SIM card, SD card, or in an NFC chip.



Telecommunications providers such as CMCC (China Mobile Communication Corporation), Vodafone or AT&T usually use the SIM card solution, encouraging their subscribers to free replace old SIM cards with new ones equipped with NFC.

Peer-to-peer mode

Two NFC devices can easily interact with each other directly, exchanging small files. To establish a Bluetooth / WiFi connection, you must exchange a special format XML file. In this mode, the EB is not used.

Write / read mode

In this mode, the NFC device can read and write NFC tags. A good example of this is reading information from smart posters equipped with NFC.



Introduction to Android NFC Development

Android supports NFC with two packages: android.nfc and android.nfc.tech.
The main classes in android.nfc are:
NfcManager: Android devices can be used to manage any detected NFC adapters, but since most Android devices support only one NFC adapter, NfcManager is usually called with getDefaultAdapter to access a specific adapter.
NfcAdapter works as an NFC agent, like a network adapter on a PC. With it, the phone gains access to the NFC hardware to initialize the NFC connection.
NDEF: NFC standards define a common data format called the NFC Data Exchange Format (NDEF), which can store and transfer various types of objects, starting with MIME and ending with ultra-short RTD documents such as URLs. NdefMessage and NdefRecord are two types of NDEF for the NFC forum-defined data formats to be used in the sample code.
Tag: When an Android device detects a passive object such as a shortcut, card, etc., it creates an object of type “label”, placing it further in the target object and finally sending it to the corresponding process.
The android.nfc.tech package also contains many important subclasses. These subclasses provide access to label functions that include read and write operations. Depending on the type of technology used, these classes are divided into various categories, such as NfcA, NfcB, NfcF, MifareClassic and so on.
When the NFC-enabled phone detects a tag, the delivery system automatically creates a packet of target information. If the phone has several applications that can work with this target information, the user will be shown a window asking him to select one from the list. The label delivery system defines three types of target information, in descending order of priority: NDEF_DISCOVERED, TECH_DISCOVERED, TAG_DISCOVERED.
Here we use the target filter to work with all types of information from TECH_DISCOVERED to ACTION_TECH_DISCOVERED. The nfc_tech_filter.xml file is used for all types defined in the label. Details can be found in the Android documentation . The figure below shows how to detect a tag.



Example 1. Development of an NFC application for reading / writing tags.

The following example shows the read / write functions of an NFC tag. In order to access the NFC hardware and correctly process NFC information, declare these positions in the AndroidManifest.xml file.

<uses-permission android:name="android.permission.NFC" />

The minimum version of the SDK that your application should support is 10, declare it in the AndroidManifest.xml file

<uses-sdk android:minSdkVersion="10"/>
In the onCreate function,you can apply the NfcAdapter:
public void onCreate(Bundle savedInstanceState) {
……
adapter = NfcAdapter.getDefaultAdapter(this);
……
}  

The next target call demonstrates the read function. If the system’s broadcast message is NfcAdapter.ACTION_TAG_DISCOVERED, then you can read the information and display it.

@Override
	    protected void onNewIntent(Intent intent){
	        if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
	        mytag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  // get the detected tag
	        Parcelable[] msgs =
	intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
	            NdefRecord firstRecord = ((NdefMessage)msgs[0]).getRecords()[0];
	            byte[] payload = firstRecord.getPayload();
	            int payloadLength = payload.length;
	            int langLength = payload[0];
	            int textLength = payloadLength - langLength - 1;
	            byte[] text = new byte[textLength];
	            System.arraycopy(payload, 1+langLength, text, 0, textLength);
	            Toast.makeText(this, this.getString(R.string.ok_detection)+new String(text), Toast.LENGTH_LONG).show();
	                    }
	    }

The following code demonstrates the write function. Before determining the value of mytag, you must make sure that the label is defined and only then enter your data into it.

	If (mytag==Null){
	    ……
	}
	else{
	……
	write(message.getText().toString(),mytag);
	……
	}
	    private void write(String text, Tag tag) throws IOException, FormatException {
	        NdefRecord[] records = { createRecord(text) };
	        NdefMessage  message = new NdefMessage(records);
	// Get an instance of Ndef for the tag.
	        Ndef ndef = Ndef.get(tag); // Enable I/O
	        ndef.connect(); // Write the message
	        ndef.writeNdefMessage(message); // Close the connection
	        ndef.close();
	    }

Depending on the information you read, you can perform additional actions, such as starting a task, following a link, etc.

Example 2. Development of an NFC application using MifareClassic cards

In this example, we will use MifareClassic cards and the corresponding label type for reading. MifareClassic cards are widely used for various needs, such as person identification, bus ticket, etc. In a traditional MifareClassic map, the storage area is divided into 16 zones, each zone has 4 blocks, and each block can store 16 bytes of data.
The last block in the zone is called a trailer and is usually used to store a local read / write key. It contains two keys, A and B, 6 bytes long each, by default clogged with 00 or FF, depending on the value of MifareClassic.KEY_DEFAULT.
To write to a Mifare card, you must first have the correct key value (which plays a protective role), and also successfully authenticate.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
	    package="org.reno"  
	    android:versionCode="1"  
	    android:versionName="1.0" >   
	    <uses-permission android:name="android.permission.NFC" />   
	    <uses-sdk android:minSdkVersion="14" />   
	    <uses-feature android:name="android.hardware.nfc" android:required="true" />   
	    <application  
	        android:icon="@drawable/ic_launcher"  
	        android:label="@string/app_name" >   
	        <activity  
	            android:name="org.reno.Beam"  
	            android:label="@string/app_name"  
	            android:launchMode="singleTop" >   
	            <intent-filter>   
	                <action android:name="android.intent.action.MAIN" />   
	    
	                <category android:name="android.intent.category.LAUNCHER" />   
	            </intent-filter>   
	            <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>  
	    </application>   
	</manifest>

res / xml / nfc_tech_filter.xml:

	<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 
	    <tech-list> 
	       <tech>android.nfc.tech.MifareClassic</tech> 
	    </tech-list> 
	</resources>

An example of how to read a MifareClassic card:

	private void processIntent(Intent intent) {    
	    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);   
	    for (String tech : tagFromIntent.getTechList()) {   
	        System.out.println(tech);   
	    }   
	    boolean auth = false;   
	    MifareClassic mfc = MifareClassic.get(tagFromIntent);   
	    try {   
	        String metaInfo = "";   
	        //Enable I/O operations to the tag from this TagTechnology object.   
	        mfc.connect();   
	        int type = mfc.getType(); 
	        int sectorCount = mfc.getSectorCount();   
	        String typeS = "";   
	        switch (type) {   
	        case MifareClassic.TYPE_CLASSIC:   
	            typeS = "TYPE_CLASSIC";   
	            break;   
	        case MifareClassic.TYPE_PLUS:   
	            typeS = "TYPE_PLUS";   
	            break;   
	        case MifareClassic.TYPE_PRO:   
	            typeS = "TYPE_PRO";   
	            break;   
	        case MifareClassic.TYPE_UNKNOWN:   
	            typeS = "TYPE_UNKNOWN";   
	            break;   
	        }   
	        metaInfo += "Card type:" + typeS + "n with" + sectorCount + " Sectorsn, "  
	                + mfc.getBlockCount() + " BlocksnStorage Space: " + mfc.getSize() + "Bn";   
	        for (int j = 0; j < sectorCount; j++) {   
	            //Authenticate a sector with key A.   
	            auth = mfc.authenticateSectorWithKeyA(j,   
	                    MifareClassic.KEY_DEFAULT);   
	            int bCount;   
	            int bIndex;   
	            if (auth) {   
	                metaInfo += "Sector " + j + ": Verified successfullyn";   
	                bCount = mfc.getBlockCountInSector(j);   
	                bIndex = mfc.sectorToBlock(j);   
	                for (int i = 0; i < bCount; i++) {   
	                    byte[] data = mfc.readBlock(bIndex);   
	                    metaInfo += "Block " + bIndex + " : "  
	                            + bytesToHexString(data) + "n";   
	                    bIndex++;   
	                }   
	            } else {   
	                metaInfo += "Sector " + j + ": Verified failuren";   
	            }   
	        }   
	        promt.setText(metaInfo);   
	    } catch (Exception e) {   
	        e.printStackTrace();   
	    }   
	}

About the authors

Songyue Wang and Liang Zhang are engineers at the Intel Software and Service Group who develop mobile applications, including those for Android, and optimize them for the x86 platform.