Working with NFC–Take 2

October 24, 2012

Some time ago (I’ve blogged about working with proximity devices – NFC – in Windows 8 Consumer preview: http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2012/02/29/windows-8-consumer-preview-and-visual-studio-11-beta-working-with-proximity-device-part-2-11.aspx). Then I’ve talked about a simple way performing NFC communication – using pub/sub communication pattern which is good for exchanging simple messages using PublishMessage and PublishBinaryMessage functions on ProximityDevice class.

Few days ago my client asked me to prepare a demo which uses proximity communication to transfer custom “Items” between two instances of the app (and also Windows Phone 8 version of this app). The app uses complex “items” which includes many textual fields, undefined number of attached images, locations, etc. To achieve this I’ve decided to use 2nd approach to proximity communication briefly described in my previous blog post – the TriggeredConnection and Sockets.

(I had additional “constraints” while engineered the solution: to minimize amount of changes in the app by using existing in-the-app load/save mechanisms and to be compatible as much as possible with Windows Phone 8 app and provide maximum code reuse between the platforms to avoid developing same functionality twice. Please keep in mind, that this blog post is not focused directly on dealing those constraints; I will blog more about sharing the code and my traditional “What’s New” series about Windows Phone 8 in a short while – so stay tuned).

Let’s get started. First of all – the app must specify proximity capability in app manifest:

image

The app has to support two sides of proximity communication – sender and receiver. Both called peers. In both cases, I am using PeerFinder class (form Windows.Networking.Proximity namespace). In my case I’ve decided to use Triggered discovery type which supports Tap-to-Connect scenario I needed for my purposes.

   1: public void StartConnect(string displayName)

   2: {

   3:     if (!String.IsNullOrEmpty(displayName))

   4:     {

   5:         //Optionally name the peer so it could be easily identified 

   6:         //and optionally presented to user

   7:         PeerFinder.DisplayName = displayName;

   8:     }

   9:  

  10:     if ((PeerFinder.SupportedDiscoveryTypes & PeerDiscoveryTypes.Triggered) != PeerDiscoveryTypes.Triggered)

  11:     {

  12:         //The device doesn't support tap-and-send communication

  13:         UpdateConnectionStatus(ConnectionStatus.NotSupported);

  14:         return;

  15:     }

  16:  

  17:     //Subscribe to connection triggers

  18:     PeerFinder.TriggeredConnectionStateChanged += TriggeredConnectionStateChanged;

  19:  

  20:     //Declare alternate identities (if needed and supported)

  21:     if (!PeerFinder.AlternateIdentities.ContainsKey(AlternateIdentityKey))

  22:     {

  23:         PeerFinder.AlternateIdentities.Add(AlternateIdentityKey, AlternateIdentity);

  24:     }

  25:  

  26:     PeerFinder.AllowInfrastructure = true;

  27:     PeerFinder.AllowBluetooth = true;

  28:     PeerFinder.AllowWiFiDirect = false;

  29:  

  30:     //Start listening to proximity events

  31:     PeerFinder.Start();

  32:  

  33:     UpdateConnectionStatus(ConnectionStatus.Ready);

  34: }

The code snippet above starts the connectivity. It uses enables to optionally name the peer so it could be identified by other side, checks that device support required proximity discovery mode and subscribes to connection state changes.

The code above adds AlternateIdentities (see class declaration below). Alternate identities are used to enable communication between different applications and devices. As you remember, my demo requirement was to share the “items” between two instances of the app (which has same identity on all running devices) and Windows Phone 8 version of this app. Enabling the sharing between the app running Windows 8 and the app running on Windows Phone or different app running on Windows 8 requires adding alternate identities. The actual definition of the AlternateIdentityKey in my demo looks as follows:

   1: #if NETFX_CORE

   2:     readonly string AlternateIdentityKey = "WindowsPhone";

   3:     readonly string AlternateIdentity = "{04f1e621-039f-4829-b59f-47e71c6af98d}";

   4: #elif WINDOWS_PHONE

   5:     readonly string AlternateIdentityKey = "Windows";

   6:     readonly string AlternateIdentity = "a4ec8cdf-7759-4709-93af-5d2c2688ee46_cb1hhkscw5m06!App"; //Could be found using Windows.ApplicationModel.Package.Current.Id.FamilyName

   7: #endif

This code is a part of shared code which defines AlternateIdentity for other platform and could also define the AlternateIdentity for other app running on the same platform. When compiled for Windows 8, it adds “WindowsPhone” identity key and the ID of application running on the phone; when compiled for Windows Phone 8 it adds the “Windows” identity key and the full app ID of the Windows 8 app.

Note: The app id could be obtained from app manifest (Packaging tab).

For Windows it will be the value of “Package family name” field + “!” + app ID (the namesapce and name of the App class). It also could be obtained using Windows.ApplicationModel.Package.Current.Id.FamilyName API from within the code.

For Windows Phone app it will be the value of “Product ID”.

This way PeerFinder is aware about other apps which are allowed to connect to this app. Additionally, when two devices are in proximity and the trying to establish the proximity connection, but one of the devices is not running the application (not started or in background) this alternate ID will be using by platform to identify the “target” app and pop up a toast notification asking user’s permission to start the app and establish proximity connection.

When the connection is triggered, every app responds in slightly different way (depending of the app logic). In my case the TriggeredConnectionStateChanged event handler looks as follows:

   1: private void TriggeredConnectionStateChanged(object sender, TriggeredConnectionStateChangedEventArgs e)

   2: {

   3:     switch (e.State)

   4:     {

   5:         case TriggeredConnectState.PeerFound:

   6:             UpdateConnectionStatus(ConnectionStatus.PeerFound);

   7:             break;

   8:         case TriggeredConnectState.Connecting:

   9:             UpdateConnectionStatus(ConnectionStatus.Connecting);

  10:             break;

  11:         case TriggeredConnectState.Listening:

  12:             UpdateConnectionStatus(ConnectionStatus.Listening);

  13:             break;

  14:         case TriggeredConnectState.Canceled:

  15:             UpdateConnectionStatus(ConnectionStatus.Canceled);

  16:             break;

  17:         case TriggeredConnectState.Failed:

  18:             UpdateConnectionStatus(ConnectionStatus.Failed);

  19:             break;

  20:         case TriggeredConnectState.Completed:

  21:             socket = e.Socket;

  22:             UpdateConnectionStatus(ConnectionStatus.Completed);

  23:             break;

  24:     }

  25: }

For most cases my demo just informs the user with different connectivity state changes and when connection established (Completed state) saves a Socket for future use. The main app also listens to connectivity changes and when connection is established switches to either “receive” mode or “send” mode depending on the app logic. In both cases (will show then later in the post) a socket communication will be used to send/receive raw bytes and the actual information should be first converted to bytes representation (in case of send side) and parsed back (in case of receiving side).

To leverage the code we already had in the app I’ve decided to use objects serialized with JSON serializer. This also enabled me to write pretty “abstract” code which sending/receiving the items (you will see later). The next code block has two functions – GetItemStream which serializes my “item” to JSON format and ParseNewItem which deserializes received “item” from stream back to strongly typed object:

   1: public byte[] GetItemStream(Item note)

   2: {

   3:     List<Type> types = new List<Type>();

   4:     //Not required in this case, but should be used to 

   5:     //serializing/deserializing multiple object types

   6:     types.Add(typeof(Item)); 

   7:  

   8:     DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Item)/*, types*/);

   9:     MemoryStream ms = new MemoryStream();

  10:  

  11:     ser.WriteObject(ms, note);

  12:     byte[] json = ms.ToArray();

  13:     var value = System.Text.Encoding.UTF8.GetString(json, 0, json.Length);

  14:     Debug.WriteLine("Serialized JSON object:\n" + value);

  15:     return json;

  16: }

  17:  

  18: private Item ParseNewItem(MemoryStream ms)

  19: {

  20:     List<Type> types = new List<Type>();

  21:     //Not required in this case, but should be used to 

  22:     //serializing/deserializing multiple object types

  23:     types.Add(typeof(Item)); 

  24:     DataContractJsonSerializer des = new DataContractJsonSerializer(typeof(Item)/*, types*/);

  25:     var item = (Item)des.ReadObject(ms);

  26:     item.UniqueId = DataManager.GetUniqueId();

  27:  

  28:     return item;

  29: }

Once the object has been serialized and we got a stream it is ready to be sent.

   1: public async Task SendItemAsync([ReadOnlyArray] byte[] itemBytes, int imagesCount, [ReadOnlyArray] List<byte[]> images)

   2: {

   3:     if (itemBytes != null && socket != null)

   4:     {

   5:         //Start sending the item

   6:         try

   7:         {

   8:             DataWriter dw = new DataWriter(socket.OutputStream);

   9:  

  10:             //Sending item length

  11:             dw.WriteInt32(itemBytes.Length);

  12:             await dw.StoreAsync();

  13:  

  14:             //Sending item bytes

  15:             dw.WriteBytes(itemBytes);

  16:             await dw.StoreAsync();

  17:  

  18:             if (null != images && imagesCount > 0) //The only app-specific part here...

  19:             {

  20:                 foreach (var imageBytes in images)

  21:                 {

  22:                     //Sending image length

  23:                     dw.WriteInt32(imageBytes.Length);

  24:                     await dw.StoreAsync();

  25:  

  26:                     //Sending image bytes

  27:                     dw.WriteBytes(imageBytes);

  28:                     await dw.StoreAsync();

  29:                 }

  30:             }

  31:  

  32:             //Flushing buffers to make sure we pushed all the data over "the wire"

  33:             await dw.FlushAsync();

  34:  

  35:             //Closing DataWriter

  36:             dw.DetachStream();

  37:             dw.Dispose();

  38:         }

  39:         catch (Exception ex)

  40:         {

  41:             Debug.WriteLine(ex.Message);

  42:             //Reset the connection if case of communication error

  43:             StopConnect();

  44: #if NETFX_CORE

  45:             string PeerName = "Win8_NFC Sample";

  46: #elif WINDOWS_PHONE

  47:             string PeerName = "WP8_NFC Sample";

  48: #endif

  49:             //Restart connection

  50:             StartConnect(PeerName);

  51:         }

  52:     }

  53: }

The function is relatively simple and straight forward – user DataWriter class (System.Networking.Sockets namespace) to send bytes over socketed connection. Same goes to images which were prepared in their raw/bytes format. The code snippet which prepares and calls the SendItemAsync looks as follows (and is not shared across platforms):

   1: //Get bytes from current item

   2: byte[] bytes = ProximityManager.Instance.GetItemStream(item);

   3: List<byte[]> images = null;

   4:  

   5: //If item has images attached - prepare images

   6: if (item.Images.Count > 0)

   7: {

   8:     images = new List<byte[]>();

   9:  

  10:     foreach (var imgSrc in item.Images)

  11:     {

  12:         //Read image from local storage

  13:         var filename = imgSrc.Replace("Images/", "");

  14:         var storage = Windows.Storage.ApplicationData.Current.LocalFolder;

  15:         var folder = await storage.GetFolderAsync("Images");

  16:         var file = await folder.GetFileAsync(filename);

  17:         var stream = await file.OpenReadAsync();

  18:         //Prepare image bytes

  19:         byte[] imageBytes = new byte[stream.Size];

  20:         var str = stream.AsStream();

  21:         await str.ReadAsync(imageBytes, 0, (int)stream.Size);

  22:  

  23:         images.Add(imageBytes);

  24:     }

  25: }

  26:  

  27: //Send the item

  28: await ProximityManager.Instance.SendItemAsync(bytes, item.Images.Count, images);

The second part of the equation is to receive a message. Since we know exactly how we sending the message, the receive function looks as follows:

   1: public async void ReceiveItemAsync()

   2: {

   3:     if (socket != null)

   4:     {

   5:         try

   6:         {

   7:             DataReader dr = new DataReader(socket.InputStream);

   8:  

   9:             // Receive the size of the byte array first

  10:             if (await TryLoadAsync(dr, 4))

  11:             {

  12:                 var count = dr.ReadInt32();

  13:                 uint ucount = (uint)count;

  14:  

  15:                 if (await TryLoadAsync(dr, ucount))

  16:                 {

  17:                     byte[] bytesIn = new byte[count];

  18:                     dr.ReadBytes(bytesIn);

  19:  

  20:                     //Save/parse new item

  21:                     MemoryStream ms = new MemoryStream(bytesIn);

  22:                     Item theItem = ParseNewItem(ms);

  23:  

  24:                     var imagesCount = theItem.Images.Count;

  25:                     //Receive "attached" images - app-specific functionality

  26:                     if (imagesCount > 0)

  27:                     {

  28:                         for (int i = 0; i < imagesCount; i++)

  29:                         {

  30:                             if (await TryLoadAsync(dr, 4))

  31:                             {

  32:                                 var imgSize = dr.ReadInt32();

  33:                                 uint uimgSize = (uint)imgSize;

  34:  

  35:                                 if (await TryLoadAsync(dr, uimgSize))

  36:                                 {

  37:                                     byte[] imgBytesIn = new byte[imgSize];

  38:                                     dr.ReadBytes(imgBytesIn);

  39:  

  40:                                     MemoryStream imgMs = new MemoryStream(imgBytesIn);

  41:  

  42:                                     var imgName = theItem.Images[i];

  43:                                     imgName = imgName.Replace("Images/", "");

  44:                                     var fileName = await DataManager.SaveImageAsync(imgName, imgMs.AsInputStream(), Windows.Storage.CreationCollisionOption.GenerateUniqueName);

  45:                                 }

  46:                             }

  47:                         }

  48:                     }

  49:  

  50:                     await DataManager.AddFileAsync(theItem);

  51:  

  52:                     //Raise evens, so main app "knows" about new item

  53: #if NETFX_CORE

  54:                     await ((NFCSample.App)App.Current).Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>

  55: #elif WINDOWS_PHONE

  56:                     ((WP8_NFC.App)System.Windows.Application.Current).Dispatcher.BeginInvoke((Action)(() =>

  57: #endif

  58:                     {

  59:                         //Raise ItemReceived event

  60:                         if (ItemReceived != null)

  61:                         {

  62:                             ItemReceivedEventArgs args = new ItemReceivedEventArgs();

  63:                             args.NewItem = theItem;

  64:                             ItemReceived(this, args);

  65:                         }

  66:                     }

  67: #if WINDOWS_PHONE

  68:                     )

  69: #endif

  70:                     );

  71:                 }

  72:             }

  73:  

  74:             //Detach a stream and release it

  75:             dr.DetachStream();

  76:             dr.Dispose();

  77:         }

  78:         catch (Exception ex)

  79:         {

  80:             Debug.WriteLine(ex.Message);

  81:             //Reset the connection in case of communication error

  82:             StopConnect();

  83: #if NETFX_CORE

  84:             string PeerName = "Win8_NFC Sample";

  85: #elif WINDOWS_PHONE

  86:             string PeerName = "WP8_NFC Sample";

  87: #endif

  88:             // Restart connection

  89:             StartConnect(PeerName);

  90:         }

  91:     }

  92: }

In this code I am receiving the message parts in exactly the same order as sending them.

Note, that this code is share between Windows 8 and Windows Phone 8.

Last piece of puzzle here – the helper function which reads known amount of bytes from socket:

   1: private async Task<bool> TryLoadAsync(DataReader dr, uint count)

   2: {

   3:     //Get cancellation token 

   4:     cts = new CancellationTokenSource();

   5:     CancellationToken token = cts.Token;

   6:  

   7:     bool result = false;

   8:  

   9:     try

  10:     {

  11:         var countReceived = await dr.LoadAsync(count).AsTask(token);

  12:  

  13:         if (countReceived != count)

  14:         {

  15:             //Connection error - close it

  16:             Debug.WriteLine("Bytes read {0} != {1} total bytes. Communucation error!", countReceived, count);

  17:             //Reset connection

  18:             StopConnect();

  19: #if NETFX_CORE

  20:             string PeerName = "Win8_NFC Sample";

  21: #elif WINDOWS_PHONE

  22:             string PeerName = "WP8_NFC Sample";

  23: #endif

  24:             // Restart connection

  25:             StartConnect(PeerName);

  26:             result = false;

  27:         }

  28:         else

  29:             result = true;

  30:     }

  31:     catch (TaskCanceledException)

  32:     {

  33:         result = false;

  34:     }

  35:     finally

  36:     {

  37:         cts = null;

  38:     }

  39:  

  40:     return result;

  41: }

This is it. Now my class is shared between two apps on multiple platforms and my samples can exchange complex custom items between instances on same and different platforms.

 

To see this demo working – come to BUILD 2.0 conference taking place at Redmond, WA (October 30 – November 2, 2012) or the video I will try to film and publish after the BUILD.

Also – if you are at the BUILD – feel free to find me and say “Hi”. I will be there!

 

Teaser:

I have a massive ‘What’s new in Windows Phone 8” series ready to be published after official release!

 

Stay tuned to more posts to come.

Alex

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

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=""> <s> <strike> <strong>

*

2 comments

  1. Thomas Sch.November 9, 2012 ב 15:30

    Hello,

    thank you for this very good example!
    Is it possible to get the full code (or a working example)?

    I want to connect a WP8 with a Surface (WinRT) via PeerFinder and actually nothing works 🙁

    And: My TriggeredConnectionStateChanged-Event doesnt fire – even when i want to establish a connection with ConnectAsync(PeerInformation) (it should be “TriggeredConnectState.Connecting” (?)

    Thanks in advice,
    Thomas

    Reply
  2. Alex GoleshNovember 13, 2012 ב 17:07

    Thomas – how do you identifying your peers? Are you using correct AppIDs on both platforms?

    //Alex

    Reply