| /****************************************************************************** |
| * |
| * Copyright (c) 2014 The Android Open Source Project |
| * Copyright 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 "bt_trace.h" // Legacy trace logging |
| |
| #include "bta/hf_client/bta_hf_client_int.h" |
| #include "bta/include/bta_dm_api.h" |
| #include "stack/include/l2c_api.h" |
| #include "stack/include/port_api.h" |
| #include "types/raw_address.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; |
| } |
| |
| /* Check if RFCOMM has any incoming connection to avoid collision. */ |
| RawAddress pending_bd_addr = RawAddress::kEmpty; |
| 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(BTA_SYS_CONN_OPEN, 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; |
| } |
| /* 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 */ |
| uint16_t lcid = 0; |
| RawAddress dev_addr = RawAddress::kEmpty; |
| int status = PORT_CheckConnection(client_cb->conn_handle, &dev_addr, &lcid); |
| if (status != PORT_SUCCESS) { |
| LOG(ERROR) << __func__ << ": PORT_CheckConnection returned " << 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) { |
| /* 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); |
| } |
| } |