2010/11/05

More about BlueZ HDP API

In previous post, I have shown a simple HDP sink. Sinks that just accept connections are indeed the simplest case, and probably the majority of applications will do exactly that.

But BlueZ HDP can be a source and initiate connections as well. It can, for example, "emulate" (*) an oximeter source, doing exactly what Nonin did in last post's video.

HealthDevice interface


When we only accept connections, life is easy because it's guaranteed that remote device is a valid HDP counterpart (if we are sink, it is a source for sure). We received HealthDevice objects for free via ChannelConnected signal.

In the other hand, if we want to initiate connections, we need to find who's around, which devices have been paired etc. This is carried out using the normal BlueZ APIs (org.bluez.Manager, Adapter and Device interfaces). A HDP-capable device has only two distinguishing characteristics:

a) the UUIDs property of Device has either the 0x1401 or 0x1402 UUID, which mean it is a HDP source or sink, respectively (**)

b) the device object has the HealthDevice interface.

The HealthDevice interface methods are:


bool Echo(): sends a "ping" to the device, returns a boolean indication success of failure.

This is another standard HDP feature, may be a good thing to probe if device is nearby. It is also a reasonable option if you want to probe whether a device object has the HealthDevice interface.


object CreateChannel(application, configuration): Initiates a data channel connection.

You need to supply the local HealthApplication path (that you've got when you called CreateApplication), because it identifies indirectly the data type and role that you and remote device are going to take for that channel.

For example, if you pass an oximeter source role, data channel will be created only if remote device offers the oximeter sink role. If device is both a source and a sink, or offers two or more data types, don't worry: BlueZ will still match correctly.

The configuration parameter is one of "Streaming", "Reliable" or "Any". This maps directly to the L2CAP mode that data channel is going to use. HDP imposes some rules on this; sources must choose between "Reliable" and "Streaming" ("Reliable" is the most common) and sinks are limited to "Any", because it is the source which decides. Moreover, the first channel between two given devices must be Reliable.


DestroyChannel(object): Destroys the channel whose path is passed.

Note that you don't need to destroy a channel just because a socket has closed and/or a device went out of range. Calling Acquire() again will (hopefully) reconnect at transport level. HDP channel is a session protocol, and you just destroy it when you are finished with the session.


Now, let's take a look in HealthChannel interface.

HealthChannel interface


We have already used this interface in oximeter sink example. We called Acquire() to get the channel's socket.

It does not matter if you accepted or initiated a data channel; the only way to exchange data over it is Acquire()ing the socket and using it directly.

There are a couple additional features:


Release(): This tells HDP to shut down the data channel. You achieve similar results by calling shutdown() on socket.

Note that, regardless how the channel is shot down, you are still obligated to close() the socket, because your process holds a reference to the file descriptor. You leak file descriptors if you fail to do that. The safest way to avoid leaks is to close() upon a failed recv(), as I did in last post's example.


Type property: tells whether the channel is "Reliable" or "Streaming".

Device property: tells which device this channel belongs to.

Application property: tells which (local) HealthApplication this channel is related to. This indirectly identifies channel's data type and role.


And that's it. The API is really "small".

One reason why it could be kept "small", is the reuse of BlueZ device objects (just an additional interface -- HealthDevice -- is overloaded on them). So, things like device search etc. don't need HDP-specific APIs. The developer just keeps using the preexisting BlueZ APIs.

API reference


By the way, the full documentation of this API can be found at BlueZ documentation, file health-api.txt. Other documents of interest are adapter-api.txt, device-api.txt and manager-api.txt.


(*) Actually, is not emulation because a BlueZ-powered HDP device is as good as any other. I called that "emulation" because a typical Linux computer does not have pulse and oximetry sensors.

(**) A HDP device which is source and sink at the same time (case of data concentrators) will put both UUIDs in the same SDP record, but BlueZ internal workings recognize just one UUID per record. In this case, the 'UUIDs' property would list either 0x1401 or 0x1402, but not both; until BlueZ is fixed, you need to take this into consideration. A workaround is to fetch and verify the SDP record.
blog comments powered by Disqus