One of my main demos at both Tech.Ed Australia and New Zealand this year was showcasing the new proximity capabilities within the WinRT API. Specifically I discussed two scenarios; passive NFC using un-powered tags, and an active NFC implementation between an Android handset and a Windows 8 BUILD slate.

NFC is a global technology, and any devices that subscribe to the standards can communicate using NDEF (NFC data exchange format) messaging; and most devices can handle basic data types at the OS level. A great example of this is using Android beam to send a URL or VCard to Windows 8 and instinctively Windows will launch the default app for that particular MIME type.

NFC however can go a lot deeper when we look at interaction between apps; and we can use it to transfer data (such as session data for gaming, multimedia, data records, and a whole lot more).  I’m extremely passionate over NFC as a technology, and the fact we now have managed APIs in Windows Phone 8 and Windows 8 means we should start taking advantage of it capabilities. Over my next few blog posts I want to showcase how easy this is using the WinRT API’s to parse data from a few different devices; and today I start with the Android SDK.

Getting started with Android

The first step with getting up a running with Android is to declare the intent to use NFC within the AndroidManifest.xml file. We should add the following line:

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

Once we have permission to use NFC next we want to implement it into our activity. The design objectives around NFC is to make the experience fairly seamless, which means not having a button that says “send via NFC” but instead enabling particular screens to be “beamed” between a device. To do this we extend the activity class for that particular screen we want to use to inherit from the interface CreateNdefMessageCallback. By doing so this tells the Android OS that the screen is capable of dealing with an NFC device when placed within its proximity; and by using this interface you’ll immediately see  that when you put the app over an NFC device on the correct screen the display will shrink into its beam function.

public class MainActivity extends Activity implements CreateNdefMessageCallback
{ }

Next we are going to call into the NFC API and ensure we have access to an NFC-enabled device, Once we know our device is NFC capable we are then going to set-up a call-back to for the setNdefMessageCallback to ensure that once a device has been found, and the OS responds we have a message to send. This callback expects a delegate to be provided to handle the generation of a NDEF message, and in this particular instance we are simply going to use the activity class we have already created.

    NfcAdapter mNfcAdapter;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        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);
    }

Lastly we are going to create our NDEF record to be sent via NFC. This will be handled by our delegate that is  triggered on call back from the NFC adapter, To create the record we override the createApplicationRecord method. This method is expecting an NdefMessage type to be returned, which is essentially a wrapper around the actual NdefRecord, which means we could send multiple Ndef records in a single payload if required.

To create the Ndef message we simply call the constructor on the type an pass in an array of NdefRecords. We then simply create a new NdefRecord as a single item array. In my example I have split of our NdefRecord creation into a separate method.

    @Override
    public NdefMessage createNdefMessage(NfcEvent event){
        
        HealthRecord record = new HealthRecord();
        record.Name = "Test";
        record.Id = 1;
        XStream xstream = new XStream();
        xstream.alias("HealthRecord", HealthRecord.class);
        String xml = xstream.toXML(record);
        
        NdefMessage msg;
                
                    msg = new NdefMessage(new NdefRecord[] {
                            createApplicationRecord(xml.getBytes())
                    });
                    return msg;
                
            
    } 
    
    
    private NdefRecord createApplicationRecord(byte[] payload)
    {    
        String mimeType = "teched.example";        

        byte[] mimeBytes = mimeType.getBytes();    

        NdefRecord mimeRecord = new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, mimeBytes, new byte[0], payload);
        return  mimeRecord;
        
    }

The actual NDEF record simply consists of three elements; a NDEF type which is an enumerated field and tells the device what type of message is being sent, a mimeType/namespace, and lastly the actual message payload as a byte array.

To make the serialisation between devices a little easier for the demonstration I am creating my payload by taking a simple object (HealthRecord), setting some data, and then serialising the object to XML.  I’m then taking the XML string and converting it into a byte array and appending it to my NdefRecord.  I’m also creating my own mimeType of teched.example and setting the Ndef Type as external. I’ve done this deliberately so my application can determine that the payload is the message it is expecting. If I used the MIME type application/XML or similar – the OS may intercept the message and try and launch the default application installed that can read XML.

The key thing here however is as long as you can serialise the object/media into a byte array it can be sent via NFC. It doesn’t have to be XML, JSON, or any other text based format. This is just being used to simplify the example.

This is all that is required on the Android side to send and NFC payload, and here is the complete code:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.charset.Charset;

import android.annotation.TargetApi;
import android.app.Activity;
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.util.Log;
import android.view.Menu;
import android.widget.Toast;

import com.thoughtworks.xstream.XStream;


@TargetApi(16)
public class MainActivity extends Activity
                          implements CreateNdefMessageCallback
{


    NfcAdapter mNfcAdapter;
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        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 boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
   
    @Override
    public NdefMessage createNdefMessage(NfcEvent event){
        
        HealthRecord record = new HealthRecord();
        record.Name = "Test";
        record.Id = 1;
        XStream xstream = new XStream();
        xstream.alias("HealthRecord", HealthRecord.class);
        String xml = xstream.toXML(record);
        
        NdefMessage msg;
                
                    msg = new NdefMessage(new NdefRecord[] {
                            createApplicationRecord(xml.getBytes())
                    });
                    return msg;
                
            
    }     
    
    private NdefRecord createApplicationRecord(byte[] payload)
    {    
        String mimeType = "teched.example";        

        byte[] mimeBytes = mimeType.getBytes();    

        NdefRecord mimeRecord = new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, mimeBytes, new byte[0], payload);
        return  mimeRecord;
        
    }
    
    
}

Moving to Windows 8

Surprisingly the Windows 8 side was in easier in regards to the APIs, but also a little more complicated. It’s also worth noting that at the time of writing this article the only device I know of that fully supports NFC on Windows 8 is the BUILD Samsung slates. Hopefully we’ll see a whole raft of devices appear in October to counter this.

So moving forward with Visual Studio, and a Windows 8 store app. The UX constraints in Windows 8 around NFC are not as strict, and at this time have not been defined in the documentation. When I execute demo’s such as this I tend to follow the Android principals and avoid using a “Send via NFC button” and instead make the integration more organic. In this example we will look at how we integrate with Windows to receive the above message sent from the Android device.  I’ll actually abstract out all of the logic into a service class which can then be implemented in various ways in the UI. I’ve seen NFC examples make use of toast, flyouts, pop-ups and or simply suspending the UI whilst awaiting an incoming message. By using a service class we gain this flexibility.

To start though we need to declare our intentions to use NFC in much the same way as we do in Android. We do this by adding the following to our Package.AppManifest file (this can also be done via the Visual Studio UI tools):

   <DeviceCapability Name="proximity" />

Next we’ll create our ProximityService class, and in much the same way as we did in Android we’ll ensure we have an NFC device attached to the OS. If we don’t execute this test we’ll see some nasty exceptions when we try and execute the code if we don’t have an NFC device. Once we are confident we have access to an NFC device, we then subscribe to the DeviceArrived event. This is the OS’s way of telling us that a new NFC device has arrived within proximity of our device:

public class ProximityService 
    {
        readonly ProximityDevice _device;

        public ProximityService()
        {
            _device  = ProximityDevice.GetDefault();
            if(_device != null)
            {
                _device.DeviceArrived += DeviceOnDeviceArrived;
                
            }
            
        }
 }

Next in the event handler for the DeviceArrived event we examine any incoming payload. The Windows 8 NFC APIs are of a higher level than the Andrioid ones, and provide a more abstracted way of examining the payload. So in this example, we simply tell the OS that we only want to be notified of any incoming messages from the device that conform to a particular type and namespace. In our example it is the NDEF:ext type we have used above in conjunction with out teched.example namespace. We then provide a delegate method which we’ll use to examine the payload. Windows offers us the option to subscribe to either NDEF standard messages (MIME, wkt, URL, etc.) or a selection of bespoke Windows messages that are designed around Microsoft-centric experiences such as launching an app. If you are look at cross-platform, always stick to NDEF.

 private void DeviceOnDeviceArrived(ProximityDevice sender)
        {
            sender.SubscribeForMessage("NDEF:ext.teched.example", ActiveMessageReceived);
        }

The last part of this whole process is deserialization the NFC message, and this is where I got a little confused. To understand what is going on here it is worth taking a look at this image to understand what the actual NDEF message payload comprises of:

 Ndefrecod

Within a record you receive both header information and payload information – in a similar format to a HTTP request; and in the Windows API this header information is displayed within the managed structure of the ProximityMessage object. So naturally you expect the data property of the message to have stripped this header information from the payload – but actually it hasn’t. So initially this cost me a few hours of wondering why objects weren’t deserializing correctly; and it was only when using strings I could see the payload had these pre-cursor bytes still attached. In this example I have manually removed 17 bytes from the data which is equal to the 2 bytes used as an identifier, and the 15 bytes of my namespace/MimeType.  I could refactor the code to make this a little more automated; however I’m hoping this explains the process in a simpler fashion.

Once I’ve stripped out the header data from my payload, I’m left with a byte-array I can then deserialize back into XML, and subsequently into an object similar to that I started with in my Android app.  Now I have figured out the header information issue, I could actually strip out the necessity to use XML and simply serialise the object to and from binary – but for the moment the use of strings is a great way to step through what is happening in the message transfer process.

The very last thing I do in my service class is raise my own event to say a new message has arrived. This means that my ViewModel’s can be ignorant of this whole process and simply wait for an event to be raised by the service class, and respond to it accordingly.

public event EventHandler<HealthRecordEventArgs> HealthRecordArrived;
        
        private void ActiveMessageReceived(ProximityDevice sender, ProximityMessage message)
        {
            var byteArray = message.Data.ToArray();
            var newArray = new byte[byteArray.Length - 17];
            Array.Copy(byteArray, 17, newArray, 0, newArray.Length);
            var memoryStream = new MemoryStream(newArray,true); 

            var serialiser = new System.Xml.Serialization.XmlSerializer(typeof (HealthRecord));
            var record = (HealthRecord)  serialiser.Deserialize(memoryStream);

            if(HealthRecordArrived != null)
                HealthRecordArrived(this, new HealthRecordEventArgs(record));;
        }

This is all that is required to get two platforms, from two different OS’s/vendors/languages, transferring data using proximity networking stacks. It’s a fairly simple process that adds a lot of power to your applications.

Here is the complete code for the Windows 8 service class:

using Windows.Networking.Proximity;


public class ProximityService {

readonly ProximityDevice _device; public ProximityService() { _device = ProximityDevice.GetDefault(); if(_device != null) { _device.DeviceArrived += DeviceOnDeviceArrived; } } private void DeviceOnDeviceArrived(ProximityDevice sender) { sender.SubscribeForMessage("NDEF:ext.teched.example", ActiveMessageReceived); } public event EventHandler<HealthRecordEventArgs> HealthRecordArrived; private void ActiveMessageReceived(ProximityDevice sender, ProximityMessage message) { var byteArray = message.Data.ToArray(); var newArray = new byte[byteArray.Length - 17]; Array.Copy(byteArray, 17, newArray, 0, newArray.Length); var memoryStream = new MemoryStream(newArray,true); var serialiser = new System.Xml.Serialization.XmlSerializer(typeof (HealthRecord)); var record = (HealthRecord) serialiser.Deserialize(memoryStream); if(HealthRecordArrived != null) HealthRecordArrived(this, new HealthRecordEventArgs(record));; } } public class HealthRecordEventArgs : EventArgs { public HealthRecordEventArgs(HealthRecord record) { this.HealthRecord = record; } public HealthRecord HealthRecord { get; set; } }

Tagged on:             

One thought on “Connecting Android and Windows 8 via NFC

  • November 28, 2012 at 6:38 am
    Permalink

    Hi could you please provide a link to both full code projects?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>