blob: b633bd01fba1e374df97851341bbd2c9a8f8baa9 [file] [log] [blame]
/******************************************************************************
*
* Copyright (c) 2014 The Android Open Source Project
* Copyright (C) 2003-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 action functions for the handsfree client.
*
******************************************************************************/
#include <string.h>
#include "bt_utils.h"
#include "bta_api.h"
#include "bta_dm_api.h"
#include "bta_hf_client_api.h"
#include "bta_hf_client_int.h"
#include "bta_sys.h"
#include "l2c_api.h"
#include "osi/include/compat.h"
#include "osi/include/osi.h"
#include "port_api.h"
#include "utl.h"
/*****************************************************************************
* Constants
****************************************************************************/
/* maximum length of data to read from RFCOMM */
#define BTA_HF_CLIENT_RFC_READ_MAX 512
/*******************************************************************************
*
* Function bta_hf_client_start_close
*
* Description Start the process of closing SCO and RFCOMM connection.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
p_data->hdr.layer_specific);
return;
}
/* Take the link out of sniff and set L2C idle time to 0 */
bta_dm_pm_active(client_cb->peer_addr);
L2CA_SetIdleTimeoutByBdAddr(client_cb->peer_addr, 0, BT_TRANSPORT_BR_EDR);
/* if SCO is open close SCO and wait on RFCOMM close */
if (client_cb->sco_state == BTA_HF_CLIENT_SCO_OPEN_ST) {
client_cb->sco_close_rfc = true;
} else {
bta_hf_client_rfc_do_close(p_data);
}
/* always do SCO shutdown to handle all SCO corner cases */
bta_hf_client_sco_shutdown(client_cb);
}
/*******************************************************************************
*
* Function bta_hf_client_start_open
*
* Description This starts an HF Client open.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: wrong handle to control block %d", __func__,
p_data->hdr.layer_specific);
return;
}
/* store parameters */
if (p_data) {
client_cb->peer_addr = p_data->api_open.bd_addr;
client_cb->cli_sec_mask = p_data->api_open.sec_mask;
}
/* Check if RFCOMM has any incoming connection to avoid collision. */
RawAddress pending_bd_addr;
if (PORT_IsOpening(pending_bd_addr)) {
/* Let the incoming connection goes through. */
/* Issue collision for now. */
/* We will decide what to do when we find incoming connection later.*/
bta_hf_client_collision_cback(0, BTA_ID_HS, 0, &client_cb->peer_addr);
return;
}
/* set role */
client_cb->role = BTA_HF_CLIENT_INT;
/* do service search */
bta_hf_client_do_disc(client_cb);
}
/*******************************************************************************
*
* Function bta_hf_client_rfc_open
*
* Description Handle RFCOMM channel open.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_rfc_open(tBTA_HF_CLIENT_DATA* p_data) {
APPL_TRACE_DEBUG("%s", __func__);
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
bta_sys_conn_open(BTA_ID_HS, 1, client_cb->peer_addr);
/* start SLC procedure */
bta_hf_client_slc_seq(client_cb, false);
}
/*******************************************************************************
*
* Function bta_hf_client_rfc_acp_open
*
* Description Handle RFCOMM channel open when accepting connection.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_rfc_acp_open(tBTA_HF_CLIENT_DATA* p_data) {
APPL_TRACE_DEBUG("%s", __func__);
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
uint16_t lcid;
RawAddress dev_addr;
int status;
/* set role */
client_cb->role = BTA_HF_CLIENT_ACP;
APPL_TRACE_DEBUG("%s: conn_handle %d", __func__, client_cb->conn_handle);
/* get bd addr of peer */
if (PORT_SUCCESS != (status = PORT_CheckConnection(client_cb->conn_handle,
dev_addr, &lcid))) {
APPL_TRACE_DEBUG("%s: error PORT_CheckConnection returned status %d",
__func__, status);
}
/* Collision Handling */
if (alarm_is_scheduled(client_cb->collision_timer)) {
alarm_cancel(client_cb->collision_timer);
if (dev_addr == client_cb->peer_addr) {
/* If incoming and outgoing device are same, nothing more to do. */
/* Outgoing conn will be aborted because we have successful incoming conn.
*/
} else {
/* Resume outgoing connection. */
bta_hf_client_resume_open(client_cb);
}
}
client_cb->peer_addr = dev_addr;
/* do service discovery to get features */
bta_hf_client_do_disc(client_cb);
/* continue with open processing */
bta_hf_client_rfc_open(p_data);
}
/*******************************************************************************
*
* Function bta_hf_client_rfc_fail
*
* Description RFCOMM connection failed.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_rfc_fail(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
/* reinitialize stuff */
client_cb->peer_features = 0;
client_cb->chld_features = 0;
client_cb->role = BTA_HF_CLIENT_ACP;
client_cb->svc_conn = false;
client_cb->send_at_reply = false;
client_cb->negotiated_codec = BTM_SCO_CODEC_CVSD;
bta_hf_client_at_reset(client_cb);
}
/*******************************************************************************
*
* Function bta_hf_client_disc_fail
*
* Description This function handles a discovery failure.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_disc_fail(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
}
/*******************************************************************************
*
* Function bta_hf_client_open_fail
*
* Description open connection failed.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_open_fail(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
}
/*******************************************************************************
*
* Function bta_hf_client_rfc_close
*
* Description RFCOMM connection closed.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_rfc_close(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
bta_hf_client_at_reset(client_cb);
bta_sys_conn_close(BTA_ID_HS, 1, client_cb->peer_addr);
/* call close cback */
tBTA_HF_CLIENT evt;
memset(&evt, 0, sizeof(evt));
evt.conn.bd_addr = client_cb->peer_addr;
/* if not deregistering reopen server */
if (bta_hf_client_cb_arr.deregister == false) {
/* Make sure SCO is shutdown */
bta_hf_client_sco_shutdown(client_cb);
bta_sys_sco_unuse(BTA_ID_HS, 1, client_cb->peer_addr);
}
/* else close port and deallocate scb */
else {
tBTA_HF_CLIENT evt;
memset(&evt, 0, sizeof(evt));
evt.reg.bd_addr = client_cb->peer_addr;
bta_hf_client_app_callback(BTA_HF_CLIENT_DISABLE_EVT, &evt);
}
}
/*******************************************************************************
*
* Function bta_hf_client_disc_int_res
*
* Description This function handles a discovery result when initiator.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA* p_data) {
uint16_t event = BTA_HF_CLIENT_DISC_FAIL_EVT;
APPL_TRACE_DEBUG("%s: Status: %d", __func__, p_data->disc_result.status);
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
/* if found service */
if (p_data->disc_result.status == SDP_SUCCESS ||
p_data->disc_result.status == SDP_DB_FULL) {
/* get attributes */
if (bta_hf_client_sdp_find_attr(client_cb)) {
event = BTA_HF_CLIENT_DISC_OK_EVT;
}
}
/* free discovery db */
bta_hf_client_free_db(p_data);
/* send ourselves sdp ok/fail event */
bta_hf_client_sm_execute(event, p_data);
}
/*******************************************************************************
*
* Function bta_hf_client_disc_acp_res
*
* Description This function handles a discovery result when acceptor.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_disc_acp_res(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
/* if found service */
if (p_data->disc_result.status == SDP_SUCCESS ||
p_data->disc_result.status == SDP_DB_FULL) {
/* get attributes */
bta_hf_client_sdp_find_attr(client_cb);
}
/* free discovery db */
bta_hf_client_free_db(p_data);
}
/*******************************************************************************
*
* Function bta_hf_client_rfc_data
*
* Description Read and process data from RFCOMM.
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_rfc_data(tBTA_HF_CLIENT_DATA* p_data) {
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
uint16_t len;
char buf[BTA_HF_CLIENT_RFC_READ_MAX];
memset(buf, 0, sizeof(buf));
/* read data from rfcomm; if bad status, we're done */
while (PORT_ReadData(client_cb->conn_handle, buf, BTA_HF_CLIENT_RFC_READ_MAX,
&len) == PORT_SUCCESS) {
/* if no data, we're done */
if (len == 0) {
break;
}
bta_hf_client_at_parse(client_cb, buf, len);
/* no more data to read, we're done */
if (len < BTA_HF_CLIENT_RFC_READ_MAX) {
break;
}
}
}
/*******************************************************************************
*
* Function bta_hf_client_svc_conn_open
*
* Description Service level connection opened
*
*
* Returns void
*
******************************************************************************/
void bta_hf_client_svc_conn_open(tBTA_HF_CLIENT_DATA* p_data) {
APPL_TRACE_DEBUG("%s", __func__);
tBTA_HF_CLIENT_CB* client_cb =
bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
if (client_cb == NULL) {
APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
p_data->hdr.layer_specific);
return;
}
tBTA_HF_CLIENT evt;
memset(&evt, 0, sizeof(evt));
if (!client_cb->svc_conn) {
/* set state variable */
client_cb->svc_conn = true;
/* call callback */
evt.conn.bd_addr = client_cb->peer_addr;
evt.conn.peer_feat = client_cb->peer_features;
evt.conn.chld_feat = client_cb->chld_features;
bta_hf_client_app_callback(BTA_HF_CLIENT_CONN_EVT, &evt);
}
}