Bluetooth for Dummies: Getting Data from SDP

As a result of effort in previous post we now armed with knowdledge what has to be done in order to connect to Bluetooth SDP (Service Discovery Protocol). All we need to get from SDP is a RFComm channel number to connect to, the rest information provided by SDP service while it is a subject of certain interest, not compulsory for our goal. Make the same preparations and run two consoles with logging hcpdump applications and it was described in the previous post. Let’s analyze packet exchange a bit further, immediately after establishing connection to SDP. Here is what the first console would typically report:

< ACL data: handle 43 flags 0x02 dlen 24
    L2CAP(d): cid 0x0040 len 20 [psm 1]
        SDP SSA Req: tid 0x0 len 0xf //Service search attribute request is sent to remote endpoint
          pat uuid-16 0x1101 (SP)
          max 65535
          aid(s) 0x0000 - 0xffff
          cont 00
> ACL data: handle 43 flags 0x02 dlen 50
    L2CAP(d): cid 0x0040 len 46 [psm 1]
        SDP SSA Rsp: tid 0x0 len 0x29 //Service search attribute response is received but it doesn't fit in one packet
          count 36
          cont 02 00 24 //Indication that there is still some data pending
< ACL data: handle 43 flags 0x02 dlen 26
    L2CAP(d): cid 0x0040 len 22 [psm 1]
        SDP SSA Req: tid 0x1 len 0x11 //Service search attribute request is sent again to fetch what didn't fit in the first packet
          pat uuid-16 0x1101 (SP)
          max 65535
          aid(s) 0x0000 - 0xffff
          cont 02 00 24 //Indication that we need to get the pending part of data
> ACL data: handle 43 flags 0x02 dlen 28
    L2CAP(d): cid 0x0040 len 24 [psm 1]
        SDP SSA Rsp: tid 0x1 len 0x13 //Second service search attribute response with remaining data
          count 16
          record #0
              aid 0x0000 (SrvRecHndl)
                 uint 0x10007
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x1101 (SP) >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) > <
                 uuid-16 0x0003 (RFCOMM) uint 0x10 > >
              aid 0x0009 (BTProfileDescList)
                 < < uuid-16 0x1101 (SP) uint 0x100 > >
          cont 00 //Indication that there is no pending data anymore

Let’s have a look at the same exchange in raw representation logged by the second console and try to decode it:

< 0000: 02 2a 20 18 00 14 00 40  00 06 00 00 00 0f 35 03  .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009)
  0010: 19 11 01 ff ff 35 05 0a  00 00 ff ff 00           .....5.......

> 0000: 02 2a 20 32 00 2e 00 40  00 07 00 00 00 29 00 24  .* 2...@.....).$ //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009)
  0010: 36 00 31 36 00 2e 09 00  00 0a 00 01 00 07 09 00  6.16............
  0020: 01 35 03 19 11 01 09 00  04 35 0c 35 03 19 01 00  .5.......5.5....
  0030: 35 05 19 00 02 00 24                              5.....$

< 0000: 02 2a 20 1a 00 16 00 40  00 06 00 01 00 11 35 03  .* ....@......5. //1-st SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU (0x06 at address 0x0009)
  0010: 19 11 01 ff ff 35 05 0a  00 00 ff ff 02 00 24     .....5........$

> 0000: 02 2a 20 1c 00 18 00 40  00 07 00 01 00 13 00 10  .* ....@........ //2-nd SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU (0x07 at address 0x0009)
  0010: 03 08 10 09 00 09 35 08  35 06 19 11 01 09 01 00  ......5.5.......
  0020: 00                                                .

Again, it would be worth getting familiar with internal packet structure and payload. An SDP service is basically a hierarchical list of attributes, each attribute has a list of field-value pairs and fields can have different types (byte, short, int – just like modern programming language). Attributes can be grouped and can nest more elements or groups inside. This is pretty much what we need to know about SDP services. Let’s have a closer look at the binary data:

=== HCI header ===
02 2a HCI hanle with PB and BC flags
20 18 HCI ACL total data length (it actually specifies total length 0x0018)
00 14 L2CAP payload length
00 40 SDP channel ID (please note that channel number is not 0x0001 anymore that is why the packet is interpreted as SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU 
  and not as L2CAP_CMD_DISCONNECT_REQUEST (although they both have the same code)

00  ??? Unknown byte, it is present neither in spec nor in packets when sent from Arduino/Luminardo.
  Probably a bug in hcidump as the byte is not even accounted by HCI ACL total data length, just ignore it

=== Actual L2CAP command ===
06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU
00 00 Transaction identifier
00 0f Parameter length
{
  35 Data Element Sequence, data size in next 8 bits
  03 Data size
  {
    19 Field type UUID, 2 bytes
    11 01 Field value 0x0100 - L2CAP_UUID
  }
  ff ff Maximum attribute byte count
  35 Data Element Sequence, data size in next 8 bits
  05 Data size
  {
    0a Field type Unsigned int, 4 bytes  - Attribute ID range
    00 00 Attribute range from 0x0000...
    ff ff ... to 0xFFFF
  }
}
00 No more data

In essence, the packet above is requesting all attributes with identifiers from 0 to 0xFFFF from service with UUID = 0x0100.

=== HCI header ===
02 2a 20 32 00 2e 00 40  00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
00 00 Transaction identifier
00 29 Parameter length
00 24 Attribute lists byte count
{
  36 Data Element Sequence, size in next 16 bits
  00 31 Size
  {
    36 Data Element Sequence, size in next 16 bits
    00 2e Size
    {
      09 Unsigned integer in next 16 bits
      00 00 Attr ID 0000 - "Service Record Handle"
      0a Unsigned integer in next 32 bits
      00 01 00 07 Attr Value - handle

      09 Unsigned integer in next 16 bits
      00 01 Attr ID 0001 - "Service Class ID"
      35 Data Element Sequence, size in next 8 bits
      03 Size
      {
        19 UUID, 2 bytes
        11 01 "SERIALPORT  UUID"
      }

     09 Unsigned integer in next 16 bits
     00 04 Attr ID 0004 - "Protocol Descriptor List"
     35 Data Element Sequence, size in next 8 bits
     0c Size
     {
       35 Data Element Sequence, size in next 8 bits
       03 Size
       {
         19 UUID, 2 bytes
         01 00 "L2CAP UUID"
       }

       35 Data Element Sequence, size in next 8 bits
       05 Size
       {
         19 UUID, 2 bytes
         00 ... end of data, 3 bytes are still missing, will be continued in next package
         ...
       }
}
02 Size in next two bytes
00 24 More data to come
=== HCI header ===
02 2a 20 1a 00 16 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
06 SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU
00 01 Transaction Identifier
00 11 Parameter length
{
  35 Data Element Sequence, data size in next 8 bits
  03 Data Size
  {
    19 Field type UUID, 2 bytes
    11 01 Field value 0x0100 - L2CAP_UUID
  }
  ff ff Maximum attribute byte count
  35 Data Element Sequence, data size in next 8 bits
  05 Data Size
  {
    0a Field type Unsigned int, 4 bytes  - Attribute ID range
    00 00 Attribute range from 0x0000...
    ff ff ... to 0xFFFF
  }
}
02 Copied from last response,
00 24 in essence it is a request to get the remaining data
=== HCI header ===
02 2a 20 1c 00 18 00 40 00 - HCI header, as above, please see the SDP_SERVICE_SEARCH_ATTRIBUTE_REQEUST_PDU packet structure

=== Actual L2CAP command ===
07 SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU
00 01 Transaction identifier
00 13 Parameter length
00 10 Attribute lists byte count
{
  03 Continuation of 0x00 from previous packet makes "00 03" - "RFCOMM UUID"
  08 Unsigned integer in next 8 bits
  10 Here it is, a RFComm channel number, that is what we are after!
  
  09 Unsigned integer in next 16 bits
  00 09 Attr ID 0009 - "Bluetooth Profile Descriptor List"
  35 Data Element Sequence, data size in next 8 bits
  08 Data Size
  {
    35 Data Element Sequence, data size in next 8 bits
    06 Data Size
    {
      19 UUID, 2 bytes
      11 01 "SERIALPORT  UUID"
      09 Unsigned int in next 16 bits
      01 00 Profile version number
   }
}
00 No more data

As an attentive reader has probably already noted, in the last package we received a value that we were after, it is RFComm channel number which is 0x10 for this particular case. At this point we know what has to be sent and how in order to get this magic number. Enough theories, time to put our knowledge in practice. As a prototype to validate our ideas with SDP service a limited in functionality class called SPPi has been designed, please refer to the Download section of this post. Copy SPPi.cpp and SPPi.h to USB Host 2.0 folder then compile and run Luminardo_USBHost_Bluetooth_SPPi.ino. The output should be like this (which proves that now we can talk SDP):

Luminardo USB Host Bluetooth SPPi Test
SPP Bluetooth Library Started
Enabling VBus... enabled
Bluetooth Dongle Initialized
HCI Reset complete
Write class of device
Local Bluetooth Address: 00:19:0E:12:65:6A
The name is set to: Luminardo
Pairing to 'Other' device with predefined address
Device: 40:22:11:00:69:58 has been found
Connecting to 'Other' device...
Connected to 'Other' device
Received Key Request
Bluetooth pin is set too: 1234
Pairing successful with 'Other' device
SDP Connection Request
L2CAP Connection Response
SDP Connection Response
SDP Configuration Request Received
SDP Configuration Response Sent
SDP Configuration Request Sent
SDP Configuration Response Received
SDP Successfully Configured
SDP Service Search Attribute Request 1 Sent
SDP data... - SDP Service Search Attribute Response 1: 0B 20 34 00 30 00 41 00 07 00 48 00 2B 00 26 36 00 31 36 00 2E 09 00 00 0A 00 01 00 07 09 00 01 35 03 19
11 01 09 00 04 35 0C 35 03 19 01 00 35 05 19 00 03 08 02 00 26
SDP Service Search Attribute Request 2 Sent
SDP data... - SDP Service Search Attribute Response 2: 0B 20 1A 00 16 00 41 00 07 00 49 00 11 00 0E 10 09 00 09 35 08 35 06 19 11 01 09 01 00 00

Now you can move to the next, final step – to start RFComm transport layer which will be described in detail in next post. Stay tuned and don’t hesitate to ask questions!

Downloads:

1. USB Host 2.0 Add-on Allowing to Initiate Communication with SDP service.