Sunday, 9 December 2012

A WP8 app I’m working on needs Bluetooth
image transfer capabilities built in, so I started looking at the Microsoft
examples and samples as a starting point. For starters I used the following MSDN
article:

This is not the case as a GUID format service
address must be supplied which matches one of the listed device Bluetooth capabilities:

Advanced Audio Distribution Profile (A2DP
1.2)

Audio/Video Remote Control Profile (AVRCP 1.4

Hands Free Profile (HFP 1.5)

Phone Book Access Profile (PBAP 1.1)

Object Push Profile (OPP 1.1)

Out of Band (OOB) and Near Field
Communications (NFC)

There are a couple of other examples but they aren't particularly helpful either.

I’ve never had any dealing with Bluetooth
before, but done lots of TCP and serial comms and got a few pointers from Mike
Hole to get me started. At first I looked at the various Bluetooth specs from here (I always like to see protocol specs when working on this kind of thing):

However these don’t provide the level of
detail required as we need byte-level description of the protocol. For this you need IrDA
Object Exchange Protocol (OBEX) spec which is not available without being a member of IrDA but I
found a copy after a bit of googling. There are two other profiles which looked applicable to transferring an image, FTP and BIP (Basic Imaging Profile) but the only applicable profile the phone supports is OPP, which can be used to transfer objects such as files.

Anyway, down to the code. These are a few
methods which connect to a peer device, then send bytes from a file (for me
this was an image) in packets and then disconnect.

Peer Search

This bit is the same as the examples:

privateasyncvoid Search()

{

this.IsLoading = true;

this.IsShareExecutable = false;

this.Message = Resources.StringTable.BTSearching;

// Note:
You can only browse and connect to paired devices!

//
Configure PeerFinder to search for all paired devices.

PeerFinder.AlternateIdentities["Bluetooth:Paired"] = "";

this.pairedDevices = awaitPeerFinder.FindAllPeersAsync();

if (pairedDevices.Count == 0)

{

this.Message = Resources.StringTable.BTNoDevices;

}

else

{

awaitthis.Share();

}

}

Socket Connect

In my test, once a device was found, I went
straight in to share a file from the first device:

StreamSocket _stream = null;

DataWriter _dataWriter;

DataReader _dataReader;

privateasyncTask Share()

{

this.CreatePackets();

//
Select a paired device. In this example, just pick the first one.

PeerInformation selectedDevice = pairedDevices[0];

int count = pairedDevices.Count;

//
Attempt a connection

_stream = newStreamSocket();

try

{

// Make sure ID_CAP_NETWORKING is enabled in your
WMAppManifest.xml, or the next

// line will throw an Access Denied exception.

string oopUUID = "{00001105-0000-1000-8000-00805f9b34fb}";

await _stream.ConnectAsync(selectedDevice.HostName, oopUUID);

_dataWriter = newDataWriter(_stream.OutputStream);

_dataReader = newDataReader(_stream.InputStream);

// Send
data

int maxServerPacket = awaitthis.ObexConnect();

if (maxServerPacket > 0)

{

if (await ObexPushRequest())

{

//
Success

}

else

{

// Failed

}

}

this.ObexDisconnect();

}

catch (Exception ex)

{

this.IsLoading = false;

this.Message = Resources.StringTable.BTFailed;

}

}

This method opens a connection using the OOP
UUID GUID, creates data packets, then performs an OBEX connect, followed by an
object push and finally a disconnect.

This establishes a connection and lets the
server know the clients buffering capabilities. The client buffer can be something
like 256 bytes to 64k - 1. The server then responds with it’s
details, but on the laptop seemed to send back the client buffer size.

Create Packets

This method chops the file into 1k chunks and
adds the necessary headers. The first packet has extra information about the
file like it’s name and size and the last packet has a different op-code to
indicate that the transmission has finished. The data to be transferred is in the _shareData variable.

Once the server has received a packet, it
issues an 0x90 to say continue and once complete, 0xA0 for success.

Disconnect

Once finished a disconnect message can be
sent, but it is not necessary and most devices and clients don’t implement it,
so just disposing the IO objects will probably do:

privateasyncvoid ObexDisconnect()

{

byte[] bytes = newbyte[3];

bytes[0] = 0x81;

bytes[1] = 0;

bytes[2] = 3;

_dataWriter.WriteBytes(bytes);

await _dataWriter.StoreAsync();

await _dataReader.LoadAsync(3);

byte[] response = newbyte[3];

_dataReader.ReadBytes(response);

_stream.Dispose();

_stream = null;

_dataReader.Dispose();

_dataWriter.Dispose();

}

Testing

As long as a device is paired and waiting to
accept a file, this should work nicely. In windows to wait for a file,
right-click the Bluetooth icon in the system tray and select ‘Receive a File’.
If everything works, you’ll see the file name appear and a progress bar as the
file transfers.