blob: 172875fe8c95875dfee4e01b8029b5d94f9e7c99 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
* This file contains action functions for SDP search.
******************************************************************************/
#include <arpa/inet.h>
#include <hardware/bluetooth.h>
#include <hardware/bt_sdp.h>
#include <stdlib.h>
#include <string.h>
#include "bt_common.h"
#include "bt_types.h"
#include "bta_api.h"
#include "bta_sdp_api.h"
#include "bta_sdp_int.h"
#include "bta_sys.h"
#include "btm_api.h"
#include "btm_int.h"
#include "osi/include/allocator.h"
#include "sdp_api.h"
#include "utl.h"
/*****************************************************************************
* Constants
****************************************************************************/
static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {
0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_SAP[] = {0x00, 0x00, 0x11, 0x2D, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
0x5F, 0x9B, 0x34, 0xFB};
// TODO:
// Both the fact that the UUIDs are declared in multiple places, plus the fact
// that there is a mess of UUID comparison and shortening methods will have to
// be fixed.
// The btcore->uuid module should be used for all instances.
#define UUID_MAX_LENGTH 16
#define IS_UUID(u1, u2) !memcmp(u1, u2, UUID_MAX_LENGTH)
static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID* u) {
static uint8_t bt_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
0x5F, 0x9B, 0x34, 0xFB};
APPL_TRACE_DEBUG("%s() - uuid len:%d", __func__, u->len);
if (u->len != 16) return *u;
if (memcmp(&u->uu.uuid128[4], &bt_base_uuid[4], 12) != 0) return *u;
tBT_UUID su;
memset(&su, 0, sizeof(su));
if (u->uu.uuid128[0] == 0 && u->uu.uuid128[1] == 0) {
su.len = 2;
uint16_t u16;
memcpy(&u16, &u->uu.uuid128[2], sizeof(u16));
su.uu.uuid16 = ntohs(u16);
} else {
su.len = 4;
uint32_t u32;
memcpy(&u32, &u->uu.uuid128[0], sizeof(u32));
su.uu.uuid32 = ntohl(u32);
}
return su;
}
static void bta_create_mns_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR* p_attr;
tSDP_PROTOCOL_ELEM pe;
uint16_t pversion = 0;
record->mns.hdr.type = SDP_TYPE_MAP_MNS;
record->mns.hdr.service_name_length = 0;
record->mns.hdr.service_name = NULL;
record->mns.hdr.rfcomm_channel_number = 0;
record->mns.hdr.l2cap_psm = -1;
record->mns.hdr.profile_version = 0;
record->mns.supported_features = 0x0000001F; // default value if not found
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
if (p_attr != NULL) {
record->mns.supported_features = p_attr->attr_value.v.u32;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->mns.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->mns.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE,
&pversion)) {
record->mns.hdr.profile_version = pversion;
}
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->mns.hdr.rfcomm_channel_number = pe.params[0];
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM);
if (p_attr != NULL) {
record->mns.hdr.l2cap_psm = p_attr->attr_value.v.u16;
}
}
static void bta_create_mas_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR* p_attr;
tSDP_PROTOCOL_ELEM pe;
uint16_t pversion = -1;
record->mas.hdr.type = SDP_TYPE_MAP_MAS;
record->mas.hdr.service_name_length = 0;
record->mas.hdr.service_name = NULL;
record->mas.hdr.rfcomm_channel_number = 0;
record->mas.hdr.l2cap_psm = -1;
record->mas.hdr.profile_version = 0;
record->mas.mas_instance_id = 0;
record->mas.supported_features = 0x0000001F;
record->mas.supported_message_types = 0;
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAS_INSTANCE_ID);
if (p_attr != NULL) {
record->mas.mas_instance_id = p_attr->attr_value.v.u8;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_MSG_TYPE);
if (p_attr != NULL) {
record->mas.supported_message_types = p_attr->attr_value.v.u8;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
if (p_attr != NULL) {
record->mas.supported_features = p_attr->attr_value.v.u32;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->mas.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->mas.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE,
&pversion)) {
record->mas.hdr.profile_version = pversion;
}
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->mas.hdr.rfcomm_channel_number = pe.params[0];
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM);
if (p_attr != NULL) {
record->mas.hdr.l2cap_psm = p_attr->attr_value.v.u16;
}
}
static void bta_create_pse_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR* p_attr;
uint16_t pversion;
tSDP_PROTOCOL_ELEM pe;
record->pse.hdr.type = SDP_TYPE_PBAP_PSE;
record->pse.hdr.service_name_length = 0;
record->pse.hdr.service_name = NULL;
record->pse.hdr.rfcomm_channel_number = 0;
record->pse.hdr.l2cap_psm = -1;
record->pse.hdr.profile_version = 0;
record->pse.supported_features = 0x00000003;
record->pse.supported_repositories = 0;
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_REPOSITORIES);
if (p_attr != NULL) {
record->pse.supported_repositories = p_attr->attr_value.v.u8;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES);
if (p_attr != NULL) {
record->pse.supported_features = p_attr->attr_value.v.u32;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->pse.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS,
&pversion)) {
record->pse.hdr.profile_version = pversion;
}
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->pse.hdr.rfcomm_channel_number = pe.params[0];
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM);
if (p_attr != NULL) {
record->pse.hdr.l2cap_psm = p_attr->attr_value.v.u16;
}
}
static void bta_create_ops_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR *p_attr, *p_sattr;
tSDP_PROTOCOL_ELEM pe;
uint16_t pversion = -1;
record->ops.hdr.type = SDP_TYPE_OPP_SERVER;
record->ops.hdr.service_name_length = 0;
record->ops.hdr.service_name = NULL;
record->ops.hdr.rfcomm_channel_number = 0;
record->ops.hdr.l2cap_psm = -1;
record->ops.hdr.profile_version = 0;
record->ops.supported_formats_list_len = 0;
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->ops.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->ops.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_OBEX_OBJECT_PUSH,
&pversion)) {
record->ops.hdr.profile_version = pversion;
}
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->ops.hdr.rfcomm_channel_number = pe.params[0];
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM);
if (p_attr != NULL) {
record->ops.hdr.l2cap_psm = p_attr->attr_value.v.u16;
}
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FORMATS_LIST);
if (p_attr != NULL) {
/* Safety check - each entry should itself be a sequence */
if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) {
record->ops.supported_formats_list_len = 0;
APPL_TRACE_ERROR(
"%s() - supported_formats_list - wrong attribute length/type:"
" 0x%02x - expected 0x06",
__func__, p_attr->attr_len_type);
} else {
int count = 0;
/* 1 byte for type/length 1 byte for value */
record->ops.supported_formats_list_len =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type) / 2;
/* Extract each value into */
for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr != NULL;
p_sattr = p_sattr->p_next_attr) {
if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) &&
(SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 1)) {
if (count == sizeof(record->ops.supported_formats_list)) {
APPL_TRACE_ERROR(
"%s() - supported_formats_list - count overflow - "
"too many sub attributes!!",
__func__);
/* If you hit this, new formats have been added,
* update SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH */
break;
}
record->ops.supported_formats_list[count] = p_sattr->attr_value.v.u8;
count++;
} else {
APPL_TRACE_ERROR(
"%s() - supported_formats_list - wrong sub attribute "
"length/type: 0x%02x - expected 0x80",
__func__, p_sattr->attr_len_type);
break;
}
}
if (record->ops.supported_formats_list_len != count) {
APPL_TRACE_WARNING(
"%s() - supported_formats_list - Length of attribute different "
"from the actual number of sub-attributes in the sequence "
"att-length: %d - number of elements: %d",
__func__, record->ops.supported_formats_list_len, count);
}
record->ops.supported_formats_list_len = count;
}
}
}
static void bta_create_sap_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR* p_attr;
tSDP_PROTOCOL_ELEM pe;
uint16_t pversion = -1;
record->sap.hdr.type = SDP_TYPE_MAP_MAS;
record->sap.hdr.service_name_length = 0;
record->sap.hdr.service_name = NULL;
record->sap.hdr.rfcomm_channel_number = 0;
record->sap.hdr.l2cap_psm = -1;
record->sap.hdr.profile_version = 0;
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->sap.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->sap.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_SAP, &pversion)) {
record->sap.hdr.profile_version = pversion;
}
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->sap.hdr.rfcomm_channel_number = pe.params[0];
}
}
static void bta_create_raw_sdp_record(bluetooth_sdp_record* record,
tSDP_DISC_REC* p_rec) {
tSDP_DISC_ATTR* p_attr;
tSDP_PROTOCOL_ELEM pe;
record->hdr.type = SDP_TYPE_RAW;
record->hdr.service_name_length = 0;
record->hdr.service_name = NULL;
record->hdr.rfcomm_channel_number = -1;
record->hdr.l2cap_psm = -1;
record->hdr.profile_version = -1;
/* Try to extract a service name */
p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME);
if (p_attr != NULL) {
record->pse.hdr.service_name_length =
SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
}
/* Try to extract an RFCOMM channel */
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
record->pse.hdr.rfcomm_channel_number = pe.params[0];
}
record->hdr.user1_ptr_len = p_bta_sdp_cfg->p_sdp_db->raw_size;
record->hdr.user1_ptr = p_bta_sdp_cfg->p_sdp_db->raw_data;
}
/*******************************************************************************
*
* Function bta_sdp_search_cback
*
* Description Callback from btm after search is completed
*
* Returns void
*
******************************************************************************/
static void bta_sdp_search_cback(uint16_t result, void* user_data) {
tSDP_DISC_REC* p_rec = NULL;
tBTA_SDP_SEARCH_COMP evt_data;
tBTA_SDP_STATUS status = BTA_SDP_FAILURE;
int count = 0;
tBT_UUID su;
APPL_TRACE_DEBUG("%s() - res: 0x%x", __func__, result);
memset(&evt_data, 0, sizeof(evt_data));
bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
if (bta_sdp_cb.p_dm_cback == NULL) return;
evt_data.remote_addr = bta_sdp_cb.remote_addr;
tBT_UUID* uuid = (tBT_UUID*)user_data;
memcpy(&evt_data.uuid, uuid, sizeof(tBT_UUID));
su = shorten_sdp_uuid(uuid);
if (result == SDP_SUCCESS || result == SDP_DB_FULL) {
do {
p_rec = SDP_FindServiceUUIDInDb(p_bta_sdp_cfg->p_sdp_db, &su, p_rec);
/* generate the matching record data pointer */
if (p_rec != NULL) {
status = BTA_SDP_SUCCESS;
if (IS_UUID(UUID_MAP_MAS, uuid->uu.uuid128)) {
APPL_TRACE_DEBUG("%s() - found MAP (MAS) uuid", __func__);
bta_create_mas_sdp_record(&evt_data.records[count], p_rec);
} else if (IS_UUID(UUID_MAP_MNS, uuid->uu.uuid128)) {
APPL_TRACE_DEBUG("%s() - found MAP (MNS) uuid", __func__);
bta_create_mns_sdp_record(&evt_data.records[count], p_rec);
} else if (IS_UUID(UUID_PBAP_PSE, uuid->uu.uuid128)) {
APPL_TRACE_DEBUG("%s() - found PBAP (PSE) uuid", __func__);
bta_create_pse_sdp_record(&evt_data.records[count], p_rec);
} else if (IS_UUID(UUID_OBEX_OBJECT_PUSH, uuid->uu.uuid128)) {
APPL_TRACE_DEBUG("%s() - found Object Push Server (OPS) uuid",
__func__);
bta_create_ops_sdp_record(&evt_data.records[count], p_rec);
} else if (IS_UUID(UUID_SAP, uuid->uu.uuid128)) {
APPL_TRACE_DEBUG("%s() - found SAP uuid", __func__);
bta_create_sap_sdp_record(&evt_data.records[count], p_rec);
} else {
/* we do not have specific structure for this */
APPL_TRACE_DEBUG("%s() - profile not identified. using raw data",
__func__);
bta_create_raw_sdp_record(&evt_data.records[count], p_rec);
p_rec = NULL; // Terminate loop
/* For raw, we only extract the first entry, and then return the
entire
raw data chunk.
TODO: Find a way to split the raw data into record chunks, and
iterate
to extract generic data for each chunk - e.g. rfcomm channel
and
service name. */
}
count++;
} else {
APPL_TRACE_DEBUG("%s() - UUID not found", __func__);
}
} while (p_rec != NULL && count < BTA_SDP_MAX_RECORDS);
evt_data.record_count = count;
}
evt_data.status = status;
tBTA_SDP bta_sdp;
bta_sdp.sdp_search_comp = evt_data;
bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp,
(void*)&uuid->uu.uuid128);
osi_free(user_data); // We no longer need the user data to track the search
}
/*******************************************************************************
*
* Function bta_sdp_enable
*
* Description Initializes the SDP I/F
*
* Returns void
*
******************************************************************************/
void bta_sdp_enable(tBTA_SDP_MSG* p_data) {
APPL_TRACE_DEBUG("%s in, sdp_active:%d", __func__, bta_sdp_cb.sdp_active);
tBTA_SDP_STATUS status = BTA_SDP_SUCCESS;
bta_sdp_cb.p_dm_cback = p_data->enable.p_cback;
tBTA_SDP bta_sdp;
bta_sdp.status = status;
bta_sdp_cb.p_dm_cback(BTA_SDP_ENABLE_EVT, &bta_sdp, NULL);
}
/*******************************************************************************
*
* Function bta_sdp_search
*
* Description Discovers all sdp records for an uuid on remote device
*
* Returns void
*
******************************************************************************/
void bta_sdp_search(tBTA_SDP_MSG* p_data) {
if (p_data == NULL) {
APPL_TRACE_DEBUG("SDP control block handle is null");
return;
}
tBTA_SDP_STATUS status = BTA_SDP_FAILURE;
APPL_TRACE_DEBUG("%s in, sdp_active:%d", __func__, bta_sdp_cb.sdp_active);
if (bta_sdp_cb.sdp_active != BTA_SDP_ACTIVE_NONE) {
/* SDP is still in progress */
status = BTA_SDP_BUSY;
if (bta_sdp_cb.p_dm_cback) {
tBTA_SDP_SEARCH_COMP result;
memset(&result, 0, sizeof(result));
result.uuid = p_data->get_search.uuid;
result.remote_addr = p_data->get_search.bd_addr;
result.status = status;
tBTA_SDP bta_sdp;
bta_sdp.sdp_search_comp = result;
bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp, NULL);
}
return;
}
bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_YES;
bta_sdp_cb.remote_addr = p_data->get_search.bd_addr;
/* set the uuid used in the search */
tBT_UUID* bta_sdp_search_uuid =
static_cast<tBT_UUID*>(osi_malloc(sizeof(tBT_UUID)));
memcpy(bta_sdp_search_uuid, &(p_data->get_search.uuid), sizeof(tBT_UUID));
/* initialize the search for the uuid */
APPL_TRACE_DEBUG("%s init discovery with UUID(len: %d):", __func__,
bta_sdp_search_uuid->len);
for (int x = 0; x < bta_sdp_search_uuid->len; x++) {
APPL_TRACE_DEBUG("%X", bta_sdp_search_uuid->uu.uuid128[x]);
}
SDP_InitDiscoveryDb(p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1,
bta_sdp_search_uuid, 0, NULL);
if (!SDP_ServiceSearchAttributeRequest2(
p_data->get_search.bd_addr, p_bta_sdp_cfg->p_sdp_db,
bta_sdp_search_cback, (void*)bta_sdp_search_uuid)) {
bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
/* failed to start SDP. report the failure right away */
if (bta_sdp_cb.p_dm_cback) {
tBTA_SDP_SEARCH_COMP result;
memset(&result, 0, sizeof(result));
result.uuid = p_data->get_search.uuid;
result.remote_addr = p_data->get_search.bd_addr;
result.status = status;
tBTA_SDP bta_sdp;
bta_sdp.sdp_search_comp = result;
bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, &bta_sdp, NULL);
}
}
/*
else report the result when the cback is called
*/
}
/*******************************************************************************
*
* Function bta_sdp_record
*
* Description Discovers all sdp records for an uuid on remote device
*
* Returns void
*
******************************************************************************/
void bta_sdp_create_record(tBTA_SDP_MSG* p_data) {
APPL_TRACE_DEBUG("%s() event: %d", __func__, p_data->record.hdr.event);
if (bta_sdp_cb.p_dm_cback)
bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, NULL,
p_data->record.user_data);
}
/*******************************************************************************
*
* Function bta_sdp_create_record
*
* Description Discovers all sdp records for an uuid on remote device
*
* Returns void
*
******************************************************************************/
void bta_sdp_remove_record(tBTA_SDP_MSG* p_data) {
APPL_TRACE_DEBUG("%s() event: %d", __func__, p_data->record.hdr.event);
if (bta_sdp_cb.p_dm_cback)
bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, NULL,
p_data->record.user_data);
}