Description
Service discovery mechanism provide the means for clients to discovery the existence of services provided by hosts as well as the attributes of those services. When searching for services, clients needs to specify the wanted pattern(service attribute characteristics). The pattern is a list of UUIDs used to locate matching service records.
Each service record has an identifier called handle(a 32-bit number) that uniquely identifies each service record within an SDP server. The search procedure consist of two steps:
- Retrieve the handles: Using GetRemoteServiceHandles a client can retrieve the handles for the records that implement the wanted UUID (characteristic).
- Retrieve the record: GetRemoteServiceRecord/!GetRemoteServiceRecordAsXML returns the record in the binary/XML format.
Those methods are basically a mapping of Service Search (SS) and Service Attribute (SA) transactions. For more details read the Bluetooth core specification. The specification defines a third optional transaction called Service Search Attribute (SSA), it is an unified transaction where the user send the wanted pattern and receive a record list that match the pattern.
Search patterns
The pattern is data element sequence where each element in the sequence is a UUID. Currently, the BlueZ D-Bus API supports only a single pattern. It can be UUID-128 or friendly names. Friendly names are hard-coded in hcid, therefore UUID-128 format is more suitable.
The first UUID defined by the Bluetooth Assigned Numbers document is known as the Bluetooth Base UUID and has the value 00000000-0000-1000-8000-00805F9B34FB. Here are some UUID examples:
- PNP: 00001200-0000-1000-8000-00805F9B34FB
- HID: 00001124-0000-1000-8000-00805F9B34FB
- Headset: 00001108-0000-1000-8000-00805F9B34FB
Sending a empty string the hcid will consider that the client wants a public browse query.
Currently, the supported friendly service names are:
- vcp: Video Conference
- pbap: Phone Book Access
- sap: SIM Access
- ftp: OBEX File Transfer
- bpp: Printing
- bip: Imaging
- synch: Synchronization
- dun: Dial-Up Networking
- opp: OBEX Object Push
- fax: Fax
- spp: Serial Port
- hsp: Headset
- panu: PAN User
- nap: Network Access Point
Development warnings/recommendations
TBD: multiple requests, public browse, device class and service class
XML representation
<?xml version="1.0" encoding="UTF-8" ?>
<record>
<attribute id="0x0000">
<uint32 value="0x00010013" />
</attribute>
<attribute id="0x0001">
<sequence>
<sequence>
<uuid value="00005557-0000-1000-8000-0002ee000001" />
</sequence>
</sequence>
</attribute>
<attribute id="0x0004">
<sequence>
<sequence>
<uuid value="0x0100" />
</sequence>
<sequence>
<uuid value="0x0003" />
<uint8 value="0x01" />
</sequence>
</sequence>
</attribute>
<attribute id="0x0005">
<sequence>
<sequence>
<uuid value="0x1002" />
</sequence>
</sequence>
</attribute>
</record>
Python examples
import dbus bus = dbus.SystemBus(); obj = bus.get_object('org.bluez', '/org/bluez/hci0') adapter = dbus.Interface(obj, 'org.bluez.Adapter') # Change XX:XX:XX:XX:XX:XX to a valid address, empty string to service brownse handles = adapter.GetRemoteServiceHandles('XX:XX:XX:XX:XX:XX', '') for handle in handles: # Change XX:XX:XX:XX:XX:XX to a valid address print adapter.GetRemoteServiceRecordAsXML('XX:XX:XX:XX:XX:XX', handle)
D-Bus glib example
This is an example function retrieving channel for specified service
gint
get_remote_service_channel (DBusGProxy *adapter_proxy, const gchar *address, const gchar *uuid)
{
GError *error = NULL;
GArray *handle_array = NULL;
GArray *record_array = NULL;
sdp_record_t *sdp_record = NULL;
gint scanned;
sdp_list_t *protos = NULL;
gint channel = -1;
/* find services that match our UUID */
if (!dbus_g_proxy_call (adapter_proxy,
"GetRemoteServiceHandles", &error,
G_TYPE_STRING, address,
G_TYPE_STRING, uuid,
G_TYPE_INVALID,
DBUS_TYPE_G_UINT_ARRAY, &handle_array,
G_TYPE_INVALID)) {
g_warning (error->message);
g_error_free (error);
goto out;
}
if (!handle_array) {
goto out;
}
/* get service record for the first service handle */
if (!dbus_g_proxy_call (adapter_proxy,
"GetRemoteServiceRecord", &error,
G_TYPE_STRING, address,
G_TYPE_UINT, *((guint32 *)handle_array->data),
G_TYPE_INVALID,
DBUS_TYPE_G_UCHAR_ARRAY, &record_array,
G_TYPE_INVALID)) {
g_warning (error->message);
g_error_free (error);
goto out;
}
if (!record_array) {
goto out;
}
sdp_record = sdp_extract_pdu ((uint8_t *)record_array->data, &scanned);
/* get channel for this service */
if (sdp_get_access_protos (sdp_record, &protos) != 0) {
goto out;
}
channel = sdp_get_proto_port (protos, RFCOMM_UUID);
out:
if (protos) {
sdp_list_foreach (protos, (sdp_list_func_t)sdp_list_free, 0);
sdp_list_free (protos, 0);
}
if (sdp_record)
sdp_record_free (sdp_record);
return channel;
}
Extracting binary records
Converting from binary format to sdp_record_t
int scanned; sdp_record_t *record; ... /* Could be the reply of GetRemoteServiceRecord */ dbus_message_iter_init(msg, &iter); dbus_message_iter_recurse(&iter, &array); dbus_message_iter_get_fixed_array(&array, &binary_record, &size); record = sdp_extract_pdu(binary_record, &scanned); if (!record) { /* Failed */ ... } if (size != scanned) { /* Size mismatch of service record */ ... } sdp_record_free(record);
XML to SDP record
The sdp_xml_parse_record function belongs to BlueZ utils helper library
sdp_record_t *record; char buf[2048]; ... /* Reading the XML file or Received from GetRemoteServiceRecordAsXML and writing to buf */ ... record = sdp_xml_parse_record(buf, strlen(buf)); if (!record) { /* Parsing XML failed */ } sdp_record_free(record);
SDP record to binary format
This is an example how convert from sdp_record_t to binary format.
static int update_record(uint32_t handle, sdp_record_t *rec) { DBusMessage *msg, *reply; DBusError derr; sdp_buf_t buf; if (sdp_gen_record_pdu(rec, &buf) < 0) return -1; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "UpdateServiceRecord"); if (!msg) return -ENOMEM; dbus_message_append_args(msg, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &buf.data, buf.data_size, DBUS_TYPE_INVALID); ... return 0; }
dbus-send examples
# Public Browse $dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.GetRemoteServiceHandles string:"00:14:51:58:A5:6C" string:"" # Searching serial port profile $dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.GetRemoteServiceHandles string:"00:14:51:58:A5:6C" string:"spp" # Getting the record $dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.GetRemoteServiceRecord string:"00:14:51:58:A5:6C" uint32:65540 # Getting the record in the XML format $dbus-send --system --print-reply --dest=org.bluez /org/bluez/hci0 org.bluez.Adapter.GetRemoteServiceRecordAsXML string:"00:14:51:58:A5:6C" uint32:65540
