/****************************************************************************** * * Copyright 1999-2012 Broadcom Corporation * * 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 the main SDP functions * ******************************************************************************/ #include #include "bt_common.h" #include "bt_target.h" #include "hcidefs.h" #include "l2c_api.h" #include "l2cdefs.h" #include "osi/include/osi.h" #include "sdp_api.h" #include "sdpint.h" #include "stack/btm/btm_sec.h" /** M: Serialize SDP requests @{ */ #include "mediatek/stack/include/mtk_sdp.h" #include "stack/l2cap/l2c_int.h" /** @} */ /******************************************************************************/ /* G L O B A L S D P D A T A */ /******************************************************************************/ tSDP_CB sdp_cb; /******************************************************************************/ /* L O C A L F U N C T I O N P R O T O T Y P E S */ /******************************************************************************/ static void sdp_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid, UNUSED_ATTR uint16_t psm, uint8_t l2cap_id); static void sdp_config_ind(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg); static void sdp_config_cfm(uint16_t l2cap_cid, uint16_t result, tL2CAP_CFG_INFO* p_cfg); static void sdp_disconnect_ind(uint16_t l2cap_cid, bool ack_needed); static void sdp_data_ind(uint16_t l2cap_cid, BT_HDR* p_msg); static void sdp_connect_cfm(uint16_t l2cap_cid, uint16_t result); static void sdp_on_l2cap_error(uint16_t l2cap_cid, uint16_t result); static void sdp_disconnect_cfm(uint16_t l2cap_cid, UNUSED_ATTR uint16_t result); /******************************************************************************* * * Function sdp_init * * Description This function initializes the SDP unit. * * Returns void * ******************************************************************************/ void sdp_init(void) { /* Clears all structures and local SDP database (if Server is enabled) */ memset(&sdp_cb, 0, sizeof(tSDP_CB)); for (int i = 0; i < SDP_MAX_CONNECTIONS; i++) { sdp_cb.ccb[i].sdp_conn_timer = alarm_new("sdp.sdp_conn_timer"); } /* Initialize the L2CAP configuration. We only care about MTU */ sdp_cb.l2cap_my_cfg.mtu_present = true; sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE; sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16; sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS; sdp_cb.trace_level = BT_TRACE_LEVEL_WARNING; sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind; sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm; sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind; sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm; sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind; sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind; sdp_cb.reg_info.pL2CA_Error_Cb = sdp_on_l2cap_error; sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm; /* Now, register with L2CAP */ if (!L2CA_Register2(BT_PSM_SDP, sdp_cb.reg_info, true /* enable_snoop */, nullptr, SDP_MTU_SIZE, 0, BTM_SEC_NONE)) { SDP_TRACE_ERROR("SDP Registration failed"); } } void sdp_free(void) { for (int i = 0; i < SDP_MAX_CONNECTIONS; i++) { alarm_free(sdp_cb.ccb[i].sdp_conn_timer); sdp_cb.ccb[i].sdp_conn_timer = NULL; } } /******************************************************************************* * * Function sdp_connect_ind * * Description This function handles an inbound connection indication * from L2CAP. This is the case where we are acting as a * server. * * Returns void * ******************************************************************************/ static void sdp_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid, UNUSED_ATTR uint16_t psm, uint8_t l2cap_id) { tCONN_CB* p_ccb = sdpu_allocate_ccb(); if (p_ccb == NULL) return; /* Transition to the next appropriate state, waiting for config setup. */ p_ccb->con_state = SDP_STATE_CFG_SETUP; /* Save the BD Address and Channel ID. */ p_ccb->device_address = bd_addr; p_ccb->connection_id = l2cap_cid; } static void sdp_on_l2cap_error(uint16_t l2cap_cid, uint16_t result) { SDP_TRACE_EVENT("SDP - sdp_on_l2cap_error"); tCONN_CB* p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == nullptr) return; sdp_disconnect(p_ccb, SDP_CFG_FAILED); } /******************************************************************************* * * Function sdp_connect_cfm * * Description This function handles the connect confirm events * from L2CAP. This is the case when we are acting as a * client and have sent a connect request. * * Returns void * ******************************************************************************/ static void sdp_connect_cfm(uint16_t l2cap_cid, uint16_t result) { tCONN_CB* p_ccb; /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == NULL) { SDP_TRACE_WARNING("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid); return; } /* If the connection response contains success status, then */ /* Transition to the next state and startup the timer. */ if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP)) { p_ccb->con_state = SDP_STATE_CFG_SETUP; } else { LOG(ERROR) << __func__ << ": invoked with non OK status"; SDP_TRACE_WARNING("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x", result, p_ccb->connection_id); /* Tell the user if he has a callback */ if (p_ccb->p_cb || p_ccb->p_cb2) { tSDP_STATUS err = SDP_CONN_FAILED; if ((result == HCI_ERR_HOST_REJECT_SECURITY) || (result == HCI_ERR_AUTH_FAILURE) || (result == HCI_ERR_PAIRING_NOT_ALLOWED) || (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || (result == HCI_ERR_KEY_MISSING)) err = SDP_SECURITY_ERR; else if (result == HCI_ERR_HOST_REJECT_DEVICE) err = SDP_CONN_REJECTED; else err = SDP_CONN_FAILED; if (p_ccb->p_cb) (*p_ccb->p_cb)(err); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)(err, p_ccb->user_data); } /** M: Serialize SDP requests @{ */ sdpu_process_pend_ccb(p_ccb->connection_id, false); /** @} */ sdpu_release_ccb(p_ccb); } } /******************************************************************************* * * Function sdp_config_ind * * Description This function processes the L2CAP configuration indication * event. * * Returns void * ******************************************************************************/ static void sdp_config_ind(uint16_t l2cap_cid, tL2CAP_CFG_INFO* p_cfg) { tCONN_CB* p_ccb; /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == NULL) { SDP_TRACE_WARNING("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); return; } /* Remember the remote MTU size */ if (!p_cfg->mtu_present) { /* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */ p_ccb->rem_mtu_size = (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE) ? SDP_MTU_SIZE : L2CAP_DEFAULT_MTU; } else { if (p_cfg->mtu > SDP_MTU_SIZE) p_ccb->rem_mtu_size = SDP_MTU_SIZE; else p_ccb->rem_mtu_size = p_cfg->mtu; } SDP_TRACE_EVENT("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); } /******************************************************************************* * * Function sdp_config_cfm * * Description This function processes the L2CAP configuration confirmation * event. * * Returns void * ******************************************************************************/ static void sdp_config_cfm(uint16_t l2cap_cid, uint16_t initiator, tL2CAP_CFG_INFO* p_cfg) { sdp_config_ind(l2cap_cid, p_cfg); tCONN_CB* p_ccb; SDP_TRACE_EVENT("SDP - Rcvd cfg cfm, CID: 0x%x", l2cap_cid); /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == NULL) { SDP_TRACE_WARNING("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); return; } /* For now, always accept configuration from the other side */ p_ccb->con_state = SDP_STATE_CONNECTED; if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { sdp_disc_connected(p_ccb); } else { /* Start inactivity timer */ alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, sdp_conn_timer_timeout, p_ccb); } } /******************************************************************************* * * Function sdp_disconnect_ind * * Description This function handles a disconnect event from L2CAP. If * requested to, we ack the disconnect before dropping the CCB * * Returns void * ******************************************************************************/ static void sdp_disconnect_ind(uint16_t l2cap_cid, bool ack_needed) { tCONN_CB* p_ccb; /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == NULL) { SDP_TRACE_WARNING("SDP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); return; } SDP_TRACE_EVENT("SDP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); /* Tell the user if he has a callback */ if (p_ccb->p_cb) { /** M: fix unexcepted change of pairng addr @{ */ if ((p_ccb->con_state == SDP_STATE_CONNECTED) && (p_ccb->disc_state != SDP_DISC_WAIT_SEARCH_ATTR) && (p_ccb->disc_state != SDP_DISC_WAIT_HANDLES)) (*p_ccb->p_cb) (SDP_SUCCESS); else (*p_ccb->p_cb) (SDP_CONN_FAILED); /** @} */ } else if (p_ccb->p_cb2) (*p_ccb->p_cb2)( ((p_ccb->con_state == SDP_STATE_CONNECTED) ? SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data); /** M: Serialize SDP requests @{ */ if (ack_needed) { SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc, process pend sdp ccb: 0x%x", l2cap_cid); sdpu_process_pend_ccb(p_ccb->connection_id, false); } else { SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc, ACL disc clear pend sdp ccb: 0x%x", l2cap_cid); sdpu_clear_pend_ccb(p_ccb->connection_id); } /** @} */ sdpu_release_ccb(p_ccb); } /******************************************************************************* * * Function sdp_data_ind * * Description This function is called when data is received from L2CAP. * if we are the originator of the connection, we are the SDP * client, and the received message is queued for the client. * * If we are the destination of the connection, we are the SDP * server, so the message is passed to the server processing * function. * * Returns void * ******************************************************************************/ static void sdp_data_ind(uint16_t l2cap_cid, BT_HDR* p_msg) { tCONN_CB* p_ccb; /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb != NULL) { if (p_ccb->con_state == SDP_STATE_CONNECTED) { if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) sdp_disc_server_rsp(p_ccb, p_msg); else sdp_server_handle_client_req(p_ccb, p_msg); } else { SDP_TRACE_WARNING( "SDP - Ignored L2CAP data while in state: %d, CID: 0x%x", p_ccb->con_state, l2cap_cid); } } else { SDP_TRACE_WARNING("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); } osi_free(p_msg); } /******************************************************************************* * * Function sdp_conn_originate * * Description This function is called from the API to originate a * connection. * * Returns void * ******************************************************************************/ tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr) { tCONN_CB* p_ccb; uint16_t cid; /* Allocate a new CCB. Return if none available. */ p_ccb = sdpu_allocate_ccb(); if (p_ccb == NULL) { SDP_TRACE_WARNING("%s: no spare CCB for peer %s", __func__, p_bd_addr.ToString().c_str()); return (NULL); } SDP_TRACE_EVENT("%s: SDP - Originate started for peer %s", __func__, p_bd_addr.ToString().c_str()); /** M: Serialize SDP requests @{ */ cid = sdpu_get_active_ccb_cid (p_bd_addr); /** @} */ /* We are the originator of this connection */ p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; /* Save the BD Address and Channel ID. */ p_ccb->device_address = p_bd_addr; /* Transition to the next appropriate state, waiting for connection confirm. */ /** M: Serialize SDP requests @{ */ if (!cid) { p_ccb->con_state = SDP_STATE_CONN_SETUP; cid = L2CA_ConnectReq (BT_PSM_SDP, p_bd_addr); } else { p_ccb->con_state = SDP_STATE_CONN_PEND; SDP_TRACE_WARNING ("one SDP - active on cid = %0x ", cid); } /** @} */ /* Check if L2CAP started the connection process */ if (cid == 0) { SDP_TRACE_WARNING("%s: SDP - Originate failed for peer %s", __func__, p_bd_addr.ToString().c_str()); sdpu_release_ccb(p_ccb); return (NULL); } p_ccb->connection_id = cid; return (p_ccb); } /******************************************************************************* * * Function sdp_disconnect * * Description This function disconnects a connection. * * Returns void * ******************************************************************************/ void sdp_disconnect(tCONN_CB* p_ccb, tSDP_REASON reason) { SDP_TRACE_EVENT("SDP - disconnect CID: 0x%x reason 0x%x", p_ccb->connection_id, reason); /* Check if we have a connection ID */ if (p_ccb->connection_id != 0) { p_ccb->disconnect_reason = reason; /** M: Serialize SDP requests @{ */ if (SDP_SUCCESS == p_ccb->disconnect_reason && (true == sdpu_process_pend_ccb(p_ccb->connection_id, true))) { /* Tell the user if he has a callback */ SDP_TRACE_EVENT("SDP - disconnect reuse cid"); if (p_ccb->p_cb) (*p_ccb->p_cb)((tSDP_REASON)p_ccb->disconnect_reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)((tSDP_REASON)p_ccb->disconnect_reason, p_ccb->user_data); sdpu_release_ccb(p_ccb); return; } else if (SDP_CANCEL == (tSDP_REASON)p_ccb->disconnect_reason && (p_ccb->con_state == SDP_STATE_CONN_SETUP)) { SDP_TRACE_EVENT("SDP - disconnect link not up or remote sdp conn rsp pending"); L2CA_DisconnectReq(p_ccb->connection_id); sdpu_process_pend_ccb(p_ccb->connection_id, false); /* Tell the user if he has a callback */ if (p_ccb->p_cb) (*p_ccb->p_cb)((tSDP_REASON)p_ccb->disconnect_reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)((tSDP_REASON)p_ccb->disconnect_reason, p_ccb->user_data); sdpu_release_ccb(p_ccb); return; } else if (SDP_CANCEL == (tSDP_REASON)p_ccb->disconnect_reason && (p_ccb->con_state == SDP_STATE_CONN_PEND)) { SDP_TRACE_EVENT("SDP - disconnect sdp cancel in pending state"); /* Tell the user if he has a callback */ if (p_ccb->p_cb) (*p_ccb->p_cb)((tSDP_REASON)p_ccb->disconnect_reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)((tSDP_REASON)p_ccb->disconnect_reason, p_ccb->user_data); sdpu_release_ccb(p_ccb); } else if (SDP_CFG_FAILED == (tSDP_REASON)p_ccb->disconnect_reason) { SDP_TRACE_EVENT("SDP - disconnect SDP_CFG_FAILED"); if (!L2CA_DisconnectReq(p_ccb->connection_id)) { tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(p_ccb->device_address, BT_TRANSPORT_BR_EDR); /* Tell the user if he has a callback */ if (p_ccb->p_cb) (*p_ccb->p_cb)((tSDP_REASON)p_ccb->disconnect_reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)((tSDP_REASON)p_ccb->disconnect_reason, p_ccb->user_data); if(p_lcb == NULL || p_lcb->link_state == LST_DISCONNECTING) { SDP_TRACE_EVENT("SDP - link is disconnecting, clear pend ccb"); sdpu_clear_pend_ccb(p_ccb->connection_id); } else { sdpu_process_pend_ccb(p_ccb->connection_id, false); } sdpu_release_ccb(p_ccb); } } else { SDP_TRACE_EVENT("SDP - disconnect L2CA_DisconnectReq"); L2CA_DisconnectReq(p_ccb->connection_id); } /** @} */ } } /******************************************************************************* * * Function sdp_disconnect_cfm * * Description This function handles a disconnect confirm event from L2CAP. * * Returns void * ******************************************************************************/ static void sdp_disconnect_cfm(uint16_t l2cap_cid, UNUSED_ATTR uint16_t result) { tCONN_CB* p_ccb; /* Find CCB based on CID */ p_ccb = sdpu_find_ccb_by_cid(l2cap_cid); if (p_ccb == NULL) { SDP_TRACE_WARNING("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); sdpu_process_pend_ccb(l2cap_cid, false); return; } SDP_TRACE_EVENT("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); /* Tell the user if he has a callback */ if (p_ccb->p_cb) (*p_ccb->p_cb)((tSDP_REASON)p_ccb->disconnect_reason); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)((tSDP_REASON)p_ccb->disconnect_reason, p_ccb->user_data); /** M: Serialize SDP requests @{ */ if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) sdpu_process_pend_ccb(p_ccb->connection_id, false); /** @} */ sdpu_release_ccb(p_ccb); } /******************************************************************************* * * Function sdp_conn_timer_timeout * * Description This function processes a timeout. Currently, we simply send * a disconnect request to L2CAP. * * Returns void * ******************************************************************************/ void sdp_conn_timer_timeout(void* data) { tCONN_CB* p_ccb = (tCONN_CB*)data; SDP_TRACE_EVENT("SDP - CCB timeout in state: %d CID: 0x%x", p_ccb->con_state, p_ccb->connection_id); L2CA_DisconnectReq(p_ccb->connection_id); /* Tell the user if there is a callback */ if (p_ccb->p_cb) (*p_ccb->p_cb)(SDP_CONN_FAILED); else if (p_ccb->p_cb2) (*p_ccb->p_cb2)(SDP_CONN_FAILED, p_ccb->user_data); /** M: Serialize SDP requests @{ */ sdpu_clear_pend_ccb(p_ccb->connection_id); /** @} */ sdpu_release_ccb(p_ccb); }