If you read my post about getting .Net working with BlueZ 4, you, like me, were probably left feeling a little... dirty. Interop is never fun in .Net, even less so when it involves patching and building Mono from source. The whole setup felt very fragile, because it was.
Don't worry, BlueZ 5 is here to save us!
...if we can just figure out how to use this dbus thing.
Rather than interop/native calls, the preferred method of accessing BlueZ 5s api is dbus.
Before we get started, there's a few tools that were immensely helpful in figuring this out. The first few are obvious, the bluez docs, examples, and source. Next, a tool to browse dbus is a must, I used d-feet. Finally, a tool to view and filter syslog. I used glogg and set my filter to either "bluetooth" or "dbus" depending on what I was troubleshooting.
Previously I had used the NDesk.DBusSharp library, although it clearly was not being maintained. There is a fork on github which is slightly newer (although it seems to not be accepting pull requests right now either). I made my own fork for the purposes of this project, the reasons I will now explain.
The first issue I had was that dbus "properties" were not working as they should, this is fixed by a pending pull request.
The second issue was that it was missing the interface org.freedesktop.DBus.ObjectManager which is central to working with bluez over dbus. Arguably I could simply put this in my own assembly, but I felt as a standard interface it belongs in dbus-sharp. Additionally the Properties interface was missing the PropertiesChanged event, which bluez uses on LE devices to notify of new values.
The third, and final issue is the big one. dbus-sharp didn't implement unix file descriptor passing.
As someone who has spent the vast majority of his career on windows, my response to this last one was.... wait.... what?
To understand what's going on here, let's start with the bluez docs for the profile service, specifically the Profile1.NewConnection API.
void NewConnection(object device, fd, dict fd_properties) This method gets called when a new service level connection has been made and authorized. Common fd_properties: uint16 Version Profile version (optional) uint16 Features Profile features (optional) Possible errors: org.bluez.Error.Rejected org.bluez.Error.Canceled
So clearly this is a method signature, but what is this "fd" paramater? The short answer is it's a 4 byte integer. The long answer is... it's complicated.
When a device connects to bluez, bluez opens a socket to that device and makes it available via a file descriptor which is identified with a simple integer. It then passes this "handle" over dbus to your application which is listening for the "NewConnection" method. Easy enough, right?
The dbus specification talks a little bit about negotiating the ability to pass file descriptors, but gives very little detail about how it is actually done.
Due to security concerns, you can't just pass pointers around between applications willy nilly, the kernel needs to be involved to transfer the ownership of the handle. dbus itself runs on top of unix sockets, and unix sockets have a special mechanism for transferring file descriptors (among other things) called socket control messages. Coincidentally, there was a pending pull request to have socket control message functions added to mono.posix right when I needed it.
Once socket control messages were added to mono.posix, my job of implementing file descriptor passing in dbus became much easier (debugging interop is never fun). The dbus specs helped, but the main help was looking at other implementations of dbus. this go implementation was very helpful.
The dbus message with the method call sends a 4 byte index in place of the file descriptor parameter. This index is then used to fetch the file descriptor from the socket control messages, and retrieve the actual integer file descriptor which can then be manipulated as any other unix socket, in my case I open it as a stream to read/write data to it.
Finally, success, a .net dbus lib that can exercise all the dbus bits that bluez uses.
I began implementing a new bluetooth library for PebbleSharp to use I called Mono.BlueZ (original, I know) which exercises bluez over dbus. I'll write more about using this library in another post