sdp_client_rfcomm: add sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid

This commit is contained in:
Matthias Ringwald 2020-12-11 22:07:25 +01:00
parent 17b0e9ff31
commit 18aab41867
4 changed files with 211 additions and 65 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
CC256x: With ENABLE_CC256X_ASSISTED_HFP, HFP enables WBS codec on demand and configures PCM/I2S interface for 8kH/16kHz
SDP Client RFCOMM: add `sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid`
### Fixed
L2CAP: fix packet size check for incoming classic basic channels (regression introduced in v1.2.1)

View File

@ -53,41 +53,63 @@
#include "classic/sdp_util.h"
#include "hci_cmd.h"
#if (SDP_SERVICE_NAME_LEN < 16)
#error "SDP_SERVICE_NAME_LEN must be at least 16 bytes"
#endif
// called by test/sdp_client
void sdp_client_query_rfcomm_init(void);
typedef enum {
static enum {
GET_PROTOCOL_LIST_LENGTH = 1,
GET_PROTOCOL_LENGTH,
GET_PROTOCOL_ID_HEADER_LENGTH,
GET_PROTOCOL_ID,
GET_PROTOCOL_VALUE_LENGTH,
GET_PROTOCOL_VALUE
} pdl_state_t;
} protokoll_descriptor_list_state;
static enum {
GET_SERVICE_LIST_LENGTH = 1,
GET_SERVICE_LIST_ITEM_GET_UUID_TYPE,
GET_SERVICE_LIST_ITEM,
GET_SERVICE_LIST_ITEM_SHORT,
GET_SERVICE_LIST_ITEM_LONG,
GET_SERVICE_INVALID,
} service_class_id_list_state;
// higher layer query - get rfcomm channel and name
// All attributes: 0x0001 - 0x0100
static const uint8_t des_attributeIDList[] = { 0x35, 0x05, 0x0A, 0x00, 0x01, 0x01, 0x00};
static uint8_t sdp_service_name[SDP_SERVICE_NAME_LEN+1];
static uint8_t sdp_service_name_len = 0;
static uint8_t sdp_rfcomm_channel_nr = 0;
static uint8_t sdp_service_name_header_size;
static uint8_t sdp_service_name[SDP_SERVICE_NAME_LEN+1];
static uint8_t sdp_service_name_len = 0;
static uint8_t sdp_rfcomm_channel_nr = 0;
static uint8_t sdp_service_name_header_size;
static bool sdp_client_rfcomm_serviceclass_matched;
static bool sdp_client_rfcomm_match_serviceclass;
static uint16_t sdp_client_rfcomm_uuid16;
static int protocol_value_bytes_received = 0;
static int protocol_value_size;
static int protocol_offset;
static int protocol_size;
static int protocol_id_bytes_to_read;
static uint32_t protocol_id = 0;
static pdl_state_t pdl_state = GET_PROTOCOL_LIST_LENGTH;
static int protocol_value_bytes_received = 0;
static uint16_t protocol_id = 0;
static int protocol_offset;
static int protocol_size;
static int protocol_id_bytes_to_read;
static int protocol_value_size;
static de_state_t de_header_state;
static de_state_t sn_de_header_state;
static btstack_packet_handler_t sdp_app_callback;
//
static void sdp_rfcomm_query_prepare(void){
sdp_rfcomm_channel_nr = 0;
sdp_service_name[0] = 0;
sdp_client_rfcomm_serviceclass_matched = false;
}
static void sdp_rfcomm_query_emit_service(void){
uint8_t event[3+SDP_SERVICE_NAME_LEN+1];
event[0] = SDP_EVENT_QUERY_RFCOMM_SERVICE;
@ -95,8 +117,81 @@ static void sdp_rfcomm_query_emit_service(void){
event[2] = sdp_rfcomm_channel_nr;
(void)memcpy(&event[3], sdp_service_name, sdp_service_name_len);
event[3+sdp_service_name_len] = 0;
(*sdp_app_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
sdp_rfcomm_channel_nr = 0;
(*sdp_app_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static void sdp_client_query_rfcomm_handle_record_parsed(void){
if (sdp_rfcomm_channel_nr == 0) return;
if (sdp_client_rfcomm_match_serviceclass && (sdp_client_rfcomm_serviceclass_matched == false)) return;
sdp_rfcomm_query_emit_service();
sdp_rfcomm_query_prepare();
}
// Format: DE Sequence of UUIDs
static void sdp_client_query_rfcomm_handle_service_class_list_data(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){
UNUSED(attribute_value_length);
// init state on first byte
if (data_offset == 0){
service_class_id_list_state = GET_SERVICE_LIST_LENGTH;
de_state_init(&de_header_state);
}
// process data
switch(service_class_id_list_state){
case GET_SERVICE_LIST_LENGTH:
// read DES sequence header
if (!de_state_size(data, &de_header_state)) break;
service_class_id_list_state = GET_SERVICE_LIST_ITEM_GET_UUID_TYPE;
break;
case GET_SERVICE_LIST_ITEM_GET_UUID_TYPE:
protocol_id = 0;
protocol_offset = 0;
// validate UUID type
if (de_get_element_type(&data) != DE_UUID) {
service_class_id_list_state = GET_SERVICE_INVALID;
break;
}
// get UUID length
protocol_id_bytes_to_read = de_get_data_size(&data);
if (protocol_id_bytes_to_read > 16) {
service_class_id_list_state = GET_SERVICE_INVALID;
break;
}
service_class_id_list_state = GET_SERVICE_LIST_ITEM;
break;
case GET_SERVICE_LIST_ITEM:
sdp_service_name[protocol_offset++] = data;
protocol_id_bytes_to_read--;
if (protocol_id_bytes_to_read > 0) break;
// parse 2/4/16 bytes UUID
switch (protocol_offset){
case 2:
protocol_id = big_endian_read_16(sdp_service_name, 0);
break;
case 4:
protocol_id = big_endian_read_32(sdp_service_name, 0);
break;
case 16:
if (uuid_has_bluetooth_prefix(sdp_service_name)){
protocol_id = big_endian_read_32(sdp_service_name, 0);
}
break;
default:
break;
}
if (protocol_id == sdp_client_rfcomm_uuid16){
sdp_client_rfcomm_serviceclass_matched = true;
}
service_class_id_list_state = GET_SERVICE_LIST_ITEM_GET_UUID_TYPE;
break;
default:
break;
}
}
static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){
@ -104,16 +199,16 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
// init state on first byte
if (data_offset == 0){
pdl_state = GET_PROTOCOL_LIST_LENGTH;
de_state_init(&de_header_state);
protokoll_descriptor_list_state = GET_PROTOCOL_LIST_LENGTH;
}
switch(pdl_state){
switch(protokoll_descriptor_list_state){
case GET_PROTOCOL_LIST_LENGTH:
if (!de_state_size(data, &de_header_state)) break;
pdl_state = GET_PROTOCOL_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH;
break;
case GET_PROTOCOL_LENGTH:
@ -124,7 +219,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
protocol_offset = de_header_state.de_offset;
protocol_size = de_header_state.de_size;
pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_ID_HEADER_LENGTH;
break;
case GET_PROTOCOL_ID_HEADER_LENGTH:
@ -133,7 +228,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
protocol_id = 0;
protocol_id_bytes_to_read = de_header_state.de_size;
pdl_state = GET_PROTOCOL_ID;
protokoll_descriptor_list_state = GET_PROTOCOL_ID;
break;
@ -146,11 +241,11 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
if (protocol_offset >= protocol_size){
pdl_state = GET_PROTOCOL_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH;
break;
}
pdl_state = GET_PROTOCOL_VALUE_LENGTH;
}
protokoll_descriptor_list_state = GET_PROTOCOL_VALUE_LENGTH;
protocol_value_bytes_received = 0;
break;
@ -160,7 +255,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
if (!de_state_size(data, &de_header_state)) break;
protocol_value_size = de_header_state.de_size;
pdl_state = GET_PROTOCOL_VALUE;
protokoll_descriptor_list_state = GET_PROTOCOL_VALUE;
sdp_rfcomm_channel_nr = 0;
break;
@ -176,11 +271,11 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_
}
if (protocol_offset >= protocol_size) {
pdl_state = GET_PROTOCOL_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH;
break;
}
pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_ID_HEADER_LENGTH;
break;
default:
break;
@ -191,14 +286,15 @@ static void sdp_client_query_rfcomm_handle_service_name_data(uint32_t attribute_
// Get Header Len
if (data_offset == 0){
de_state_size(data, &sn_de_header_state);
sdp_service_name_header_size = sn_de_header_state.addon_header_bytes + 1;
de_state_init(&de_header_state);
de_state_size(data, &de_header_state);
sdp_service_name_header_size = de_header_state.addon_header_bytes + 1;
return;
}
// Get Header
if (data_offset < sdp_service_name_header_size){
de_state_size(data, &sn_de_header_state);
de_state_size(data, &de_header_state);
return;
}
@ -225,7 +321,7 @@ static void sdp_client_query_rfcomm_handle_service_name_data(uint32_t attribute_
// notify on last char
if ((data_offset == (attribute_value_length - 1)) && (sdp_rfcomm_channel_nr!=0)){
sdp_rfcomm_query_emit_service();
sdp_client_query_rfcomm_handle_record_parsed();
}
}
@ -235,17 +331,20 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type,
switch (hci_event_packet_get_type(packet)){
case SDP_EVENT_QUERY_SERVICE_RECORD_HANDLE:
// handle service without a name
if (sdp_rfcomm_channel_nr){
sdp_rfcomm_query_emit_service();
}
sdp_client_query_rfcomm_handle_record_parsed();
// prepare for new record
sdp_rfcomm_channel_nr = 0;
sdp_service_name[0] = 0;
sdp_rfcomm_query_prepare();
break;
case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
switch (sdp_event_query_attribute_byte_get_attribute_id(packet)){
case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST:
if (sdp_client_rfcomm_match_serviceclass){
sdp_client_query_rfcomm_handle_service_class_list_data(sdp_event_query_attribute_byte_get_attribute_length(packet),
sdp_event_query_attribute_byte_get_data_offset(packet),
sdp_event_query_attribute_byte_get_data(packet));
}
break;
case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
// find rfcomm channel
sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(sdp_event_query_attribute_byte_get_attribute_length(packet),
@ -264,11 +363,8 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type,
}
break;
case SDP_EVENT_QUERY_COMPLETE:
// handle service without a name
if (sdp_rfcomm_channel_nr){
sdp_rfcomm_query_emit_service();
}
(*sdp_app_callback)(HCI_EVENT_PACKET, 0, packet, size);
sdp_client_query_rfcomm_handle_record_parsed();
(*sdp_app_callback)(HCI_EVENT_PACKET, 0, packet, size);
break;
default:
break;
@ -277,30 +373,41 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type,
void sdp_client_query_rfcomm_init(void){
// init
de_state_init(&de_header_state);
de_state_init(&sn_de_header_state);
pdl_state = GET_PROTOCOL_LIST_LENGTH;
protokoll_descriptor_list_state = GET_PROTOCOL_LIST_LENGTH;
protocol_offset = 0;
sdp_rfcomm_channel_nr = 0;
sdp_service_name[0] = 0;
}
// Public API
uint8_t sdp_client_query_rfcomm_channel_and_name_for_search_pattern(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){
if (!sdp_client_ready()) return SDP_QUERY_BUSY;
static uint8_t sdp_client_query_rfcomm(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){
sdp_app_callback = callback;
sdp_client_query_rfcomm_init();
return sdp_client_query(&sdp_client_query_rfcomm_handle_sdp_parser_event, remote, service_search_pattern, (uint8_t*)&des_attributeIDList[0]);
}
// Public API
uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16){
if (!sdp_client_ready()) return SDP_QUERY_BUSY;
return sdp_client_query_rfcomm_channel_and_name_for_search_pattern(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16));
sdp_client_rfcomm_match_serviceclass = false;
return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16));
}
uint8_t sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16){
if (!sdp_client_ready()) return SDP_QUERY_BUSY;
sdp_client_rfcomm_match_serviceclass = true;
sdp_client_rfcomm_uuid16 = uuid16;
return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16));
}
uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid128(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * uuid128){
if (!sdp_client_ready()) return SDP_QUERY_BUSY;
return sdp_client_query_rfcomm_channel_and_name_for_search_pattern(callback, remote, sdp_service_search_pattern_for_uuid128(uuid128));
sdp_client_rfcomm_match_serviceclass = false;
return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid128(uuid128));
}
uint8_t sdp_client_query_rfcomm_channel_and_name_for_search_pattern(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){
if (!sdp_client_ready()) return SDP_QUERY_BUSY;
sdp_client_rfcomm_match_serviceclass = false;
return sdp_client_query_rfcomm(callback, remote, service_search_pattern);
}

View File

@ -53,14 +53,29 @@ extern "C" {
/* API_START */
/**
* @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID.
* @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID anywhere.
* @note calls sdp_service_search_pattern_for_uuid16 that uses global buffer
* @param callback handler
* @param remote BD_ADDR
* @param uuid16
*/
uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid);
uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16);
/**
* @brief Searches SDP records on a remote device for RFCOMM services with a given 128-bit UUID.
/**
* @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID in its ServiceClassIDList
* @note calls sdp_service_search_pattern_for_uuid16 that uses global buffer
* @param callback handler
* @param remote BD_ADDR
* @param uuid16
*/
uint8_t sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16);
/**
* @brief Searches SDP records on a remote device for RFCOMM services with a given 128-bit UUID anywhere
* @note calls sdp_service_search_pattern_for_uuid128 that uses global buffer
* @param callback handler
* @param remote BD_ADDR
* @param uuid128
*/
uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid128(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * uuid128);

View File

@ -15,6 +15,7 @@
#include "btstack_event.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "bluetooth_sdp.h"
#include "classic/sdp_client_rfcomm.h"
#include "classic/sdp_util.h"
#include "classic/spp_server.h"
@ -109,19 +110,19 @@ TEST_GROUP(SDPClient){
void setup(void){
service_index = 0;
sdp_client_reset(); // avoid "not ready" warning
// start query using public API although data will be injected
sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, address, 0x1234);
}
};
TEST(SDPClient, QueryRFCOMMWithSyntheticData){
const char* expected_name[] = {"SDP Response Test1", "SDP Response Test2", "SDP Response Test3"};
uint8_t expected_channel[] = {10, 11, 12};
int record_nr = sizeof(expected_channel)/sizeof(uint8_t);
int i;
// start query using public API although data will be injected
sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, address, 0x1234);
de_create_sequence(spp_buffer);
for (i=0; i<record_nr; i++){
uint8_t * record_start = de_push_sequence(spp_buffer);
@ -131,7 +132,7 @@ TEST(SDPClient, QueryRFCOMMWithSyntheticData){
sdp_parser_handle_chunk(spp_buffer, de_get_len(spp_buffer));
CHECK_EQUAL(service_index, record_nr);
CHECK_EQUAL(record_nr, service_index);
for (i=0; i<service_index; i++){
STRCMP_EQUAL(expected_name[i], service_name[i]);
CHECK_EQUAL(expected_channel[i], channel_nr[i]);
@ -139,14 +140,19 @@ TEST(SDPClient, QueryRFCOMMWithSyntheticData){
}
TEST(SDPClient, QueryRFCOMMWithMacOSXData){
const char* expected_name[] = {"OBEX Object Push",
const char* expected_name[] = {"OBEX Object Push",
"Hands Free Audio Gat", "OBEX File Transfer",
"Bluetooth-PDA-Sync", "Headset Audio Gatewa"};
uint8_t expected_channel[] = {10, 2, 15, 3, 4};
// start query using public API although data will be injected
sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, address, 0x1234);
// de_dump_data_element(sdp_test_record_list);
sdp_parser_handle_chunk(sdp_test_record_list, de_get_len(sdp_test_record_list));
CHECK_EQUAL(service_index, 5);
CHECK_EQUAL(5, service_index);
int i;
for (i=0; i<service_index; i++){
STRCMP_EQUAL(expected_name[i], service_name[i]);
@ -154,6 +160,23 @@ TEST(SDPClient, QueryRFCOMMWithMacOSXData){
}
}
TEST(SDPClient, QueryClassListValid){
// start query using public API although data will be injected
sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(&handle_query_rfcomm_event, address, BLUETOOTH_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY);
// de_dump_data_element(sdp_test_record_list);
sdp_parser_handle_chunk(sdp_test_record_list, de_get_len(sdp_test_record_list));
CHECK_EQUAL(1, service_index);
}
TEST(SDPClient, QueryClassListInvalid){
// start query using public API although data will be injected
sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(&handle_query_rfcomm_event, address, BLUETOOTH_SERVICE_CLASS_HANDSFREE);
sdp_parser_handle_chunk(sdp_test_record_list, de_get_len(sdp_test_record_list));
CHECK_EQUAL(0, service_index);
}
int main (int argc, const char * argv[]){
return CommandLineTestRunner::RunAllTests(argc, argv);