You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1844 lines
59 KiB
1844 lines
59 KiB
/****************************************************************************** |
|
* |
|
* Copyright (C) 2004-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. |
|
* |
|
******************************************************************************/ |
|
|
|
#define LOG_TAG "bta_ag_cmd" |
|
|
|
/** M: New Feature for setting volume support to property. @{ */ |
|
#include <cutils/properties.h> |
|
/** @ } */ |
|
|
|
#include <ctype.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include "bt_common.h" |
|
#include "bt_target.h" |
|
#include "bt_types.h" |
|
#include "bta_ag_api.h" |
|
#include "bta_ag_at.h" |
|
#include "bta_ag_int.h" |
|
#include "bta_api.h" |
|
#include "bta_sys.h" |
|
#include "osi/include/log.h" |
|
#include "osi/include/osi.h" |
|
#include "port_api.h" |
|
#include "utl.h" |
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
#include "mediatek/include/interop_mtk.h" |
|
#include "mediatek/include/mtk_bta_ag_cmd.h" |
|
#endif |
|
/***************************************************************************** |
|
* Constants |
|
****************************************************************************/ |
|
|
|
/* Ring timeout */ |
|
/** M: reduce ring timeout for avoid special carkit no voice @{ */ |
|
#define BTA_AG_RING_TIMEOUT_MS (3 * 1000) /* 3 seconds */ |
|
/** @} */ |
|
|
|
#define BTA_AG_CMD_MAX_VAL 32767 /* Maximum value is signed 16-bit value */ |
|
|
|
/* Invalid Chld command */ |
|
#define BTA_AG_INVALID_CHLD 255 |
|
|
|
/* clip type constants */ |
|
#define BTA_AG_CLIP_TYPE_MIN 128 |
|
#define BTA_AG_CLIP_TYPE_MAX 175 |
|
#define BTA_AG_CLIP_TYPE_DEFAULT 129 |
|
#define BTA_AG_CLIP_TYPE_VOIP 255 |
|
|
|
#define COLON_IDX_4_VGSVGM 4 |
|
|
|
/* Local events which will not trigger a higher layer callback */ |
|
enum { |
|
BTA_AG_LOCAL_EVT_FIRST = 0x100, |
|
BTA_AG_LOCAL_EVT_CCWA, |
|
BTA_AG_LOCAL_EVT_CLIP, |
|
BTA_AG_LOCAL_EVT_CMER, |
|
BTA_AG_LOCAL_EVT_BRSF, |
|
BTA_AG_LOCAL_EVT_CMEE, |
|
BTA_AG_LOCAL_EVT_BIA, |
|
BTA_AG_LOCAL_EVT_BCC, |
|
}; |
|
|
|
/* AT command interpreter table for HSP */ |
|
const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] = { |
|
{"+CKPD", BTA_AG_AT_CKPD_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200}, |
|
{"+VGS", BTA_AG_SPK_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, |
|
{"+VGM", BTA_AG_MIC_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, |
|
/* End-of-table marker used to stop lookup iteration */ |
|
{"", 0, 0, 0, 0, 0}}; |
|
|
|
/* AT command interpreter table for HFP */ |
|
const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] = { |
|
{"A", BTA_AG_AT_A_EVT, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"D", BTA_AG_AT_D_EVT, BTA_AG_AT_NONE | BTA_AG_AT_FREE, BTA_AG_AT_STR, 0, |
|
0}, |
|
{"+VGS", BTA_AG_SPK_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, |
|
{"+VGM", BTA_AG_MIC_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15}, |
|
{"+CCWA", BTA_AG_LOCAL_EVT_CCWA, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, |
|
/* Consider CHLD as str to take care of indexes for ECC */ |
|
{"+CHLD", BTA_AG_AT_CHLD_EVT, BTA_AG_AT_SET | BTA_AG_AT_TEST, BTA_AG_AT_STR, |
|
0, 4}, |
|
{"+CHUP", BTA_AG_AT_CHUP_EVT, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"+CIND", BTA_AG_AT_CIND_EVT, BTA_AG_AT_READ | BTA_AG_AT_TEST, |
|
BTA_AG_AT_STR, 0, 0}, |
|
{"+CLIP", BTA_AG_LOCAL_EVT_CLIP, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, |
|
{"+CMER", BTA_AG_LOCAL_EVT_CMER, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, |
|
{"+VTS", BTA_AG_AT_VTS_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, |
|
{"+BINP", BTA_AG_AT_BINP_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 1, 1}, |
|
{"+BLDN", BTA_AG_AT_BLDN_EVT, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"+BVRA", BTA_AG_AT_BVRA_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, |
|
{"+BRSF", BTA_AG_LOCAL_EVT_BRSF, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, |
|
BTA_AG_CMD_MAX_VAL}, |
|
{"+NREC", BTA_AG_AT_NREC_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 0}, |
|
{"+CNUM", BTA_AG_AT_CNUM_EVT, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"+BTRH", BTA_AG_AT_BTRH_EVT, BTA_AG_AT_READ | BTA_AG_AT_SET, BTA_AG_AT_INT, |
|
0, 2}, |
|
{"+CLCC", BTA_AG_AT_CLCC_EVT, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"+COPS", BTA_AG_AT_COPS_EVT, BTA_AG_AT_READ | BTA_AG_AT_SET, BTA_AG_AT_STR, |
|
0, 0}, |
|
{"+CMEE", BTA_AG_LOCAL_EVT_CMEE, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1}, |
|
{"+BIA", BTA_AG_LOCAL_EVT_BIA, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 20}, |
|
{"+CBC", BTA_AG_AT_CBC_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 100}, |
|
{"+BCC", BTA_AG_LOCAL_EVT_BCC, BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}, |
|
{"+BCS", BTA_AG_AT_BCS_EVT, BTA_AG_AT_SET, BTA_AG_AT_INT, 0, |
|
BTA_AG_CMD_MAX_VAL}, |
|
{"+BIND", BTA_AG_AT_BIND_EVT, |
|
BTA_AG_AT_SET | BTA_AG_AT_READ | BTA_AG_AT_TEST, BTA_AG_AT_STR, 0, 0}, |
|
{"+BIEV", BTA_AG_AT_BIEV_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, |
|
{"+BAC", BTA_AG_AT_BAC_EVT, BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0}, |
|
/* End-of-table marker used to stop lookup iteration */ |
|
{"", 0, 0, 0, 0, 0}}; |
|
|
|
/* AT result code table element */ |
|
typedef struct { |
|
const char* result_string; /* AT result string */ |
|
size_t result_id; /* Local or BTA result id */ |
|
uint8_t arg_type; /* whether argument is int or string */ |
|
} tBTA_AG_RESULT; |
|
|
|
/* AT result code argument types */ |
|
enum { |
|
BTA_AG_RES_FMT_NONE, /* no argument */ |
|
BTA_AG_RES_FMT_INT, /* integer argument */ |
|
BTA_AG_RES_FMT_STR /* string argument */ |
|
}; |
|
|
|
/* Local AT command result codes not defined in bta_ag_api.h */ |
|
enum { |
|
BTA_AG_LOCAL_RES_FIRST = 0x0100, |
|
BTA_AG_LOCAL_RES_OK, |
|
BTA_AG_LOCAL_RES_ERROR, |
|
BTA_AG_LOCAL_RES_RING, |
|
BTA_AG_LOCAL_RES_CLIP, |
|
BTA_AG_LOCAL_RES_BRSF, |
|
BTA_AG_LOCAL_RES_CMEE, |
|
BTA_AG_LOCAL_RES_BCS |
|
}; |
|
|
|
/* AT result code constant table */ |
|
const tBTA_AG_RESULT bta_ag_result_tbl[] = { |
|
{"OK", BTA_AG_LOCAL_RES_OK, BTA_AG_RES_FMT_NONE}, |
|
{"ERROR", BTA_AG_LOCAL_RES_ERROR, BTA_AG_RES_FMT_NONE}, |
|
{"RING", BTA_AG_LOCAL_RES_RING, BTA_AG_RES_FMT_NONE}, |
|
{"+VGS: ", BTA_AG_SPK_RES, BTA_AG_RES_FMT_INT}, |
|
{"+VGM: ", BTA_AG_MIC_RES, BTA_AG_RES_FMT_INT}, |
|
{"+CCWA: ", BTA_AG_CALL_WAIT_RES, BTA_AG_RES_FMT_STR}, |
|
{"+CHLD: ", BTA_AG_IN_CALL_HELD_RES, BTA_AG_RES_FMT_STR}, |
|
{"+CIND: ", BTA_AG_CIND_RES, BTA_AG_RES_FMT_STR}, |
|
{"+CLIP: ", BTA_AG_LOCAL_RES_CLIP, BTA_AG_RES_FMT_STR}, |
|
{"+CIEV: ", BTA_AG_IND_RES, BTA_AG_RES_FMT_STR}, |
|
{"+BINP: ", BTA_AG_BINP_RES, BTA_AG_RES_FMT_STR}, |
|
{"+BVRA: ", BTA_AG_BVRA_RES, BTA_AG_RES_FMT_INT}, |
|
{"+BRSF: ", BTA_AG_LOCAL_RES_BRSF, BTA_AG_RES_FMT_INT}, |
|
{"+BSIR: ", BTA_AG_INBAND_RING_RES, BTA_AG_RES_FMT_INT}, |
|
{"+CNUM: ", BTA_AG_CNUM_RES, BTA_AG_RES_FMT_STR}, |
|
{"+BTRH: ", BTA_AG_BTRH_RES, BTA_AG_RES_FMT_INT}, |
|
{"+CLCC: ", BTA_AG_CLCC_RES, BTA_AG_RES_FMT_STR}, |
|
{"+COPS: ", BTA_AG_COPS_RES, BTA_AG_RES_FMT_STR}, |
|
{"+CME ERROR: ", BTA_AG_LOCAL_RES_CMEE, BTA_AG_RES_FMT_INT}, |
|
{"+BCS: ", BTA_AG_LOCAL_RES_BCS, BTA_AG_RES_FMT_INT}, |
|
{"+BIND: ", BTA_AG_BIND_RES, BTA_AG_RES_FMT_STR}, |
|
{"", BTA_AG_UNAT_RES, BTA_AG_RES_FMT_STR}}; |
|
|
|
static const tBTA_AG_RESULT* bta_ag_result_by_code(size_t code) { |
|
for (size_t i = 0; |
|
i != sizeof(bta_ag_result_tbl) / sizeof(bta_ag_result_tbl[0]); ++i) { |
|
if (code == bta_ag_result_tbl[i].result_id) return &bta_ag_result_tbl[i]; |
|
} |
|
return 0; |
|
} |
|
|
|
const tBTA_AG_AT_CMD* bta_ag_at_tbl[BTA_AG_NUM_IDX] = {bta_ag_hsp_cmd, |
|
bta_ag_hfp_cmd}; |
|
|
|
typedef struct { |
|
size_t result_code; |
|
size_t indicator; |
|
} tBTA_AG_INDICATOR_MAP; |
|
|
|
/* callsetup indicator value lookup table */ |
|
const tBTA_AG_INDICATOR_MAP callsetup_indicator_map[] = { |
|
{BTA_AG_IN_CALL_RES, BTA_AG_CALLSETUP_INCOMING}, |
|
{BTA_AG_CALL_WAIT_RES, BTA_AG_CALLSETUP_INCOMING}, |
|
{BTA_AG_OUT_CALL_ORIG_RES, BTA_AG_CALLSETUP_OUTGOING}, |
|
{BTA_AG_OUT_CALL_ALERT_RES, BTA_AG_CALLSETUP_ALERTING}}; |
|
|
|
static size_t bta_ag_indicator_by_result_code(size_t code) { |
|
for (size_t i = 0; |
|
i != |
|
sizeof(callsetup_indicator_map) / sizeof(callsetup_indicator_map[0]); |
|
++i) { |
|
if (code == callsetup_indicator_map[i].result_code) |
|
return callsetup_indicator_map[i].indicator; |
|
} |
|
return BTA_AG_CALLSETUP_NONE; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_result |
|
* |
|
* Description Send an AT result code. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_send_result(tBTA_AG_SCB* p_scb, size_t code, |
|
const char* p_arg, int16_t int_arg) { |
|
const tBTA_AG_RESULT* result = bta_ag_result_by_code(code); |
|
if (result == 0) { |
|
LOG_ERROR(LOG_TAG, "%s Unable to lookup result for code %zu", __func__, |
|
code); |
|
return; |
|
} |
|
|
|
char buf[BTA_AG_AT_MAX_LEN + 16]; |
|
char* p = buf; |
|
memset(buf, 0, sizeof(buf)); |
|
|
|
/* init with \r\n */ |
|
*p++ = '\r'; |
|
*p++ = '\n'; |
|
|
|
/* copy result code string */ |
|
strlcpy(p, result->result_string, sizeof(buf) - 2); |
|
|
|
if (p_scb->conn_service == BTA_AG_HSP) { |
|
/* If HSP then ":"symbol should be changed as "=" for HSP compatibility */ |
|
switch (code) { |
|
case BTA_AG_SPK_RES: |
|
case BTA_AG_MIC_RES: |
|
if (*(p + COLON_IDX_4_VGSVGM) == ':') { |
|
*(p + COLON_IDX_4_VGSVGM) = '='; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
p += strlen(result->result_string); |
|
|
|
/* copy argument if any */ |
|
if (result->arg_type == BTA_AG_RES_FMT_INT) { |
|
p += utl_itoa((uint16_t)int_arg, p); |
|
} else if (result->arg_type == BTA_AG_RES_FMT_STR) { |
|
strcpy(p, p_arg); |
|
p += strlen(p_arg); |
|
} |
|
|
|
/* finish with \r\n */ |
|
*p++ = '\r'; |
|
*p++ = '\n'; |
|
|
|
/* send to RFCOMM */ |
|
uint16_t len = 0; |
|
PORT_WriteData(p_scb->conn_handle, buf, (uint16_t)(p - buf), &len); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_ok |
|
* |
|
* Description Send an OK result code. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_send_ok(tBTA_AG_SCB* p_scb) { |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_OK, NULL, 0); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_error |
|
* |
|
* Description Send an ERROR result code. |
|
* errcode - used to send verbose errocode |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_send_error(tBTA_AG_SCB* p_scb, int16_t errcode) { |
|
/* If HFP and extended audio gateway error codes are enabled */ |
|
if (p_scb->conn_service == BTA_AG_HFP && p_scb->cmee_enabled) |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_CMEE, NULL, errcode); |
|
else |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_ERROR, NULL, 0); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_ind |
|
* |
|
* Description Send an indicator CIEV result code. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_send_ind(tBTA_AG_SCB* p_scb, uint16_t id, uint16_t value, |
|
bool on_demand) { |
|
char str[12]; |
|
char* p = str; |
|
|
|
/* If the indicator is masked out, just return */ |
|
/* Mandatory indicators can not be masked out. */ |
|
if ((p_scb->bia_masked_out & ((uint32_t)1 << id)) && |
|
((id != BTA_AG_IND_CALL) && (id != BTA_AG_IND_CALLSETUP) && |
|
(id != BTA_AG_IND_CALLHELD))) |
|
return; |
|
|
|
/* Ensure we do not send duplicate indicators if not requested by app */ |
|
/* If it was requested by app, transmit CIEV even if it is duplicate. */ |
|
if (id == BTA_AG_IND_CALL) { |
|
if ((value == p_scb->call_ind) && (on_demand == false)) return; |
|
|
|
p_scb->call_ind = (uint8_t)value; |
|
} |
|
|
|
if ((id == BTA_AG_IND_CALLSETUP) && (on_demand == false)) { |
|
if (value == p_scb->callsetup_ind) return; |
|
|
|
p_scb->callsetup_ind = (uint8_t)value; |
|
} |
|
|
|
if ((id == BTA_AG_IND_SERVICE) && (on_demand == false)) { |
|
if (value == p_scb->service_ind) return; |
|
|
|
p_scb->service_ind = (uint8_t)value; |
|
} |
|
if ((id == BTA_AG_IND_SIGNAL) && (on_demand == false)) { |
|
if (value == p_scb->signal_ind) return; |
|
|
|
p_scb->signal_ind = (uint8_t)value; |
|
} |
|
if ((id == BTA_AG_IND_ROAM) && (on_demand == false)) { |
|
if (value == p_scb->roam_ind) return; |
|
|
|
p_scb->roam_ind = (uint8_t)value; |
|
} |
|
if ((id == BTA_AG_IND_BATTCHG) && (on_demand == false)) { |
|
if (value == p_scb->battchg_ind) return; |
|
|
|
p_scb->battchg_ind = (uint8_t)value; |
|
} |
|
|
|
if ((id == BTA_AG_IND_CALLHELD) && (on_demand == false)) { |
|
/* call swap could result in sending callheld=1 multiple times */ |
|
if ((value != 1) && (value == p_scb->callheld_ind)) return; |
|
|
|
p_scb->callheld_ind = (uint8_t)value; |
|
} |
|
|
|
if (p_scb->cmer_enabled) { |
|
p += utl_itoa(id, p); |
|
*p++ = ','; |
|
utl_itoa(value, p); |
|
bta_ag_send_result(p_scb, BTA_AG_IND_RES, str, 0); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_parse_cmer |
|
* |
|
* Description Parse AT+CMER parameter string. |
|
* |
|
* |
|
* Returns true if parsed ok, false otherwise. |
|
* |
|
******************************************************************************/ |
|
static bool bta_ag_parse_cmer(char* p_s, bool* p_enabled) { |
|
int16_t n[4] = {-1, -1, -1, -1}; |
|
int i; |
|
char* p; |
|
|
|
for (i = 0; i < 4; i++) { |
|
/* skip to comma delimiter */ |
|
for (p = p_s; *p != ',' && *p != 0; p++) |
|
; |
|
|
|
/* get integer value */ |
|
*p = 0; |
|
n[i] = utl_str2int(p_s); |
|
p_s = p + 1; |
|
if (p_s == 0) { |
|
break; |
|
} |
|
} |
|
|
|
/* process values */ |
|
if (n[0] < 0 || n[3] < 0) { |
|
return false; |
|
} |
|
|
|
if ((n[0] == 3) && ((n[3] == 1) || (n[3] == 0))) { |
|
*p_enabled = (bool)n[3]; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_parse_chld |
|
* |
|
* Description Parse AT+CHLD parameter string. |
|
* |
|
* |
|
* Returns Returns idx (1-7), 0 if ECC not enabled or |
|
BTA_AG_INVALID_CHLD |
|
if idx doesn't exist/1st character of argument is not a |
|
digit |
|
* |
|
******************************************************************************/ |
|
static uint8_t bta_ag_parse_chld(UNUSED_ATTR tBTA_AG_SCB* p_scb, char* p_s) { |
|
uint8_t retval = 0; |
|
int16_t idx = -1; |
|
|
|
if (!isdigit(p_s[0])) { |
|
return BTA_AG_INVALID_CHLD; |
|
} |
|
|
|
if (p_s[1] != 0) { |
|
/* p_idxstr++; point to beginning of call number */ |
|
idx = utl_str2int(&p_s[1]); |
|
if (idx != -1 && idx < 255) { |
|
retval = (uint8_t)idx; |
|
} else { |
|
retval = BTA_AG_INVALID_CHLD; |
|
} |
|
} |
|
|
|
return (retval); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_parse_bac |
|
* |
|
* Description Parse AT+BAC parameter string. |
|
* |
|
* Returns Returns bitmap of supported codecs. |
|
* |
|
******************************************************************************/ |
|
static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB* p_scb, char* p_s) { |
|
tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE; |
|
uint16_t uuid_codec; |
|
bool cont = false; /* Continue processing */ |
|
char* p; |
|
|
|
while (p_s) { |
|
/* skip to comma delimiter */ |
|
for (p = p_s; *p != ',' && *p != 0; p++) |
|
; |
|
|
|
/* get integre value */ |
|
if (*p != 0) { |
|
*p = 0; |
|
cont = true; |
|
} else |
|
cont = false; |
|
|
|
uuid_codec = utl_str2int(p_s); |
|
switch (uuid_codec) { |
|
case UUID_CODEC_CVSD: |
|
retval |= BTA_AG_CODEC_CVSD; |
|
break; |
|
case UUID_CODEC_MSBC: |
|
retval |= BTA_AG_CODEC_MSBC; |
|
break; |
|
default: |
|
APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", uuid_codec); |
|
break; |
|
} |
|
|
|
if (cont) |
|
p_s = p + 1; |
|
else |
|
break; |
|
} |
|
|
|
return (retval); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_process_unat_res |
|
* |
|
* Description Process the unat response data and remove extra carriage |
|
* return and line feed |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
|
|
static void bta_ag_process_unat_res(char* unat_result) { |
|
uint8_t str_leng; |
|
uint8_t i = 0; |
|
uint8_t j = 0; |
|
uint8_t pairs_of_nl_cr; |
|
char trim_data[BTA_AG_AT_MAX_LEN]; |
|
|
|
str_leng = strlen(unat_result); |
|
|
|
/* If no extra CR and LF, just return */ |
|
if (str_leng < 4) return; |
|
|
|
/* Remove the carriage return and left feed */ |
|
while (unat_result[0] == '\r' && unat_result[1] == '\n' && |
|
unat_result[str_leng - 2] == '\r' && |
|
unat_result[str_leng - 1] == '\n') { |
|
pairs_of_nl_cr = 1; |
|
for (i = 0; i < (str_leng - 4 * pairs_of_nl_cr); i++) { |
|
trim_data[j++] = unat_result[i + pairs_of_nl_cr * 2]; |
|
} |
|
/* Add EOF */ |
|
trim_data[j] = '\0'; |
|
str_leng = str_leng - 4; |
|
strlcpy(unat_result, trim_data, str_leng + 1); |
|
i = 0; |
|
j = 0; |
|
|
|
if (str_leng < 4) return; |
|
} |
|
return; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_inband_enabled |
|
* |
|
* Description Determine whether in-band ring can be used. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
bool bta_ag_inband_enabled(tBTA_AG_SCB* p_scb) { |
|
/* if feature is enabled and no other scbs connected */ |
|
if (p_scb->inband_enabled && !bta_ag_other_scb_open(p_scb)) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_call_inds |
|
* |
|
* Description Send call and callsetup indicators. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_send_call_inds(tBTA_AG_SCB* p_scb, tBTA_AG_RES result) { |
|
uint8_t call = p_scb->call_ind; |
|
|
|
/* set new call and callsetup values based on BTA_AgResult */ |
|
size_t callsetup = bta_ag_indicator_by_result_code(result); |
|
|
|
if (result == BTA_AG_END_CALL_RES) { |
|
call = BTA_AG_CALL_INACTIVE; |
|
} else if (result == BTA_AG_IN_CALL_CONN_RES || |
|
result == BTA_AG_OUT_CALL_CONN_RES || |
|
result == BTA_AG_IN_CALL_HELD_RES) { |
|
call = BTA_AG_CALL_ACTIVE; |
|
} else { |
|
call = p_scb->call_ind; |
|
} |
|
|
|
/* Send indicator function tracks if the values have actually changed */ |
|
bta_ag_send_ind(p_scb, BTA_AG_IND_CALL, call, false); |
|
bta_ag_send_ind(p_scb, BTA_AG_IND_CALLSETUP, callsetup, false); |
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
|
|
switch (result) { |
|
case BTA_AG_IN_CALL_CONN_RES: |
|
if (interop_mtk_match_addr_name(INTEROP_MTK_HFP_DELAY_SCO_AFTER_ACTIVE, |
|
&(p_scb->peer_addr))) { |
|
APPL_TRACE_DEBUG("special device need delay after ciev:1,1"); |
|
usleep(500 * 1000); |
|
} |
|
break; |
|
case BTA_AG_OUT_CALL_CONN_RES: |
|
if (interop_mtk_match_addr_name(INTEROP_MTK_HFP_DELAY_SCO_IN_ACT, |
|
&(p_scb->peer_addr))) { |
|
APPL_TRACE_DEBUG("Dealy setup sco after call active"); |
|
usleep(600 * 1000); |
|
} |
|
break; |
|
default: |
|
/* ignore all others */ |
|
break; |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_at_hsp_cback |
|
* |
|
* Description AT command processing callback for HSP. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_at_hsp_cback(tBTA_AG_SCB* p_scb, uint16_t command_id, |
|
uint8_t arg_type, char* p_arg, int16_t int_arg) { |
|
APPL_TRACE_DEBUG("AT cmd:%d arg_type:%d arg:%d arg:%s", command_id, arg_type, |
|
int_arg, p_arg); |
|
|
|
bta_ag_send_ok(p_scb); |
|
|
|
tBTA_AG_VAL val; |
|
val.hdr.handle = bta_ag_scb_to_idx(p_scb); |
|
val.hdr.app_id = p_scb->app_id; |
|
val.num = (uint16_t)int_arg; |
|
strlcpy(val.str, p_arg, sizeof(val.str)); |
|
|
|
/* call callback with event */ |
|
(*bta_ag_cb.p_cback)(command_id, (tBTA_AG*)&val); |
|
} |
|
|
|
static void remove_spaces(char* str) { |
|
char* dest_str = str; |
|
|
|
while (*str) { |
|
if (*str == ' ') { |
|
str++; |
|
} else { |
|
*dest_str++ = *str++; |
|
} |
|
} |
|
*dest_str = '\0'; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_find_empty_hf_ind) |
|
* |
|
* Description This function returns the index of an empty HF indicator |
|
* structure. |
|
* |
|
* Returns int : index of the empty HF indicator structure or |
|
* -1 if no empty indicator |
|
* is available. |
|
* |
|
******************************************************************************/ |
|
static int bta_ag_find_empty_hf_ind(tBTA_AG_SCB* p_scb) { |
|
for (int index = 0; index < BTA_AG_MAX_NUM_PEER_HF_IND; index++) { |
|
if (p_scb->peer_hf_indicators[index].ind_id == 0) return index; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_find_hf_ind_by_id |
|
* |
|
* Description This function returns the index of the HF indicator |
|
* structure by the indicator id |
|
* |
|
* Returns int : index of the HF indicator structure |
|
* -1 if the indicator |
|
* was not found. |
|
* |
|
******************************************************************************/ |
|
static int bta_ag_find_hf_ind_by_id(tBTA_AG_HF_IND* p_hf_ind, int size, |
|
uint32_t ind_id) { |
|
for (int index = 0; index < size; index++) { |
|
if (p_hf_ind[index].ind_id == ind_id) return index; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_parse_bind_set |
|
* |
|
* Description Parse AT+BIND set command and save the indicators |
|
* |
|
* Returns true if successful |
|
* |
|
******************************************************************************/ |
|
static bool bta_ag_parse_bind_set(tBTA_AG_SCB* p_scb, tBTA_AG_VAL val) { |
|
char* p_token = strtok(val.str, ","); |
|
if (p_token == NULL) return false; |
|
|
|
while (p_token != NULL) { |
|
uint16_t rcv_ind_id = atoi(p_token); |
|
int index = bta_ag_find_empty_hf_ind(p_scb); |
|
if (index == -1) { |
|
APPL_TRACE_WARNING("%s Can't save more indicators", __func__); |
|
return false; |
|
} |
|
|
|
p_scb->peer_hf_indicators[index].ind_id = rcv_ind_id; |
|
APPL_TRACE_DEBUG("%s peer_hf_ind[%d] = %d", __func__, index, rcv_ind_id); |
|
|
|
p_token = strtok(NULL, ","); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_bind_response |
|
* |
|
* Description Send response for the AT+BIND command (HFP 1.7) received |
|
* from the headset based on the argument types. |
|
* |
|
* Returns Void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_bind_response(tBTA_AG_SCB* p_scb, uint8_t arg_type) { |
|
char buffer[BTA_AG_AT_MAX_LEN]; |
|
memset(buffer, 0, BTA_AG_AT_MAX_LEN); |
|
|
|
if (arg_type == BTA_AG_AT_TEST) { |
|
int index = 0; |
|
buffer[index++] = '('; |
|
|
|
for (uint32_t i = 0; i < bta_ag_local_hf_ind_cfg[0].ind_id; i++) { |
|
if (bta_ag_local_hf_ind_cfg[i + 1].is_supported) { |
|
/* Add ',' from second indicator */ |
|
if (index > 1) buffer[index++] = ','; |
|
snprintf(&buffer[index++], 2, "%d", |
|
bta_ag_local_hf_ind_cfg[i + 1].ind_id); |
|
} |
|
} |
|
|
|
buffer[index++] = ')'; |
|
|
|
bta_ag_send_result(p_scb, BTA_AG_BIND_RES, buffer, 0); |
|
bta_ag_send_ok(p_scb); |
|
} else if (arg_type == BTA_AG_AT_READ) { |
|
char* p = buffer; |
|
|
|
/* bta_ag_local_hf_ind_cfg[0].ind_id is used as BTA_AG_NUM_LOCAL_HF_IND */ |
|
for (uint32_t i = 0; i < bta_ag_local_hf_ind_cfg[0].ind_id; i++) { |
|
if (i == BTA_AG_MAX_NUM_LOCAL_HF_IND) { |
|
APPL_TRACE_WARNING("%s No space for more HF indicators", __func__); |
|
break; |
|
} |
|
|
|
p_scb->local_hf_indicators[i].ind_id = |
|
bta_ag_local_hf_ind_cfg[i + 1].ind_id; |
|
p_scb->local_hf_indicators[i].is_supported = |
|
bta_ag_local_hf_ind_cfg[i + 1].is_supported; |
|
p_scb->local_hf_indicators[i].is_enable = |
|
bta_ag_local_hf_ind_cfg[i + 1].is_enable; |
|
|
|
int peer_index = bta_ag_find_hf_ind_by_id( |
|
p_scb->peer_hf_indicators, BTA_AG_MAX_NUM_PEER_HF_IND, |
|
p_scb->local_hf_indicators[i].ind_id); |
|
|
|
/* Check whether local and peer sides support this indicator */ |
|
if (p_scb->local_hf_indicators[i].is_supported == true && |
|
peer_index != -1) { |
|
/* In the format of ind, state */ |
|
p += utl_itoa((uint16_t)p_scb->local_hf_indicators[i].ind_id, p); |
|
*p++ = ','; |
|
p += utl_itoa((uint16_t)p_scb->local_hf_indicators[i].is_enable, p); |
|
|
|
bta_ag_send_result(p_scb, BTA_AG_BIND_RES, buffer, 0); |
|
|
|
memset(buffer, 0, sizeof(buffer)); |
|
p = buffer; |
|
} else { |
|
/* If indicator is not supported, also set it to disable */ |
|
p_scb->local_hf_indicators[i].is_enable = false; |
|
} |
|
} |
|
|
|
bta_ag_send_ok(p_scb); |
|
|
|
/* If the service level connection wan't already open, now it's open */ |
|
if (!p_scb->svc_conn) bta_ag_svc_conn_open(p_scb, NULL); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_parse_biev_response |
|
* |
|
* Description Send response for AT+BIEV command (HFP 1.7) received from |
|
* the headset based on the argument types. |
|
* |
|
* Returns true if the response was parsed successfully |
|
* |
|
******************************************************************************/ |
|
static bool bta_ag_parse_biev_response(tBTA_AG_SCB* p_scb, tBTA_AG_VAL* val) { |
|
char* p_token = strtok(val->str, ","); |
|
uint16_t rcv_ind_id = atoi(p_token); |
|
|
|
p_token = strtok(NULL, ","); |
|
uint16_t rcv_ind_val = atoi(p_token); |
|
|
|
APPL_TRACE_DEBUG("%s BIEV indicator id %d, value %d", __func__, rcv_ind_id, |
|
rcv_ind_val); |
|
|
|
/* Check whether indicator ID is valid or not */ |
|
if (rcv_ind_id > BTA_AG_NUM_LOCAL_HF_IND) { |
|
APPL_TRACE_WARNING("%s received invalid indicator id %d", __func__, |
|
rcv_ind_id); |
|
return false; |
|
} |
|
|
|
/* Check this indicator is support or not and enabled or not */ |
|
int local_index = bta_ag_find_hf_ind_by_id( |
|
p_scb->local_hf_indicators, BTA_AG_MAX_NUM_LOCAL_HF_IND, rcv_ind_id); |
|
if (local_index == -1 || |
|
p_scb->local_hf_indicators[local_index].is_supported != true || |
|
p_scb->local_hf_indicators[local_index].is_enable != true) { |
|
APPL_TRACE_WARNING("%s indicator id %d not supported or disabled", __func__, |
|
rcv_ind_id); |
|
return false; |
|
} |
|
|
|
/* For each indicator ID, check whether the indicator value is in range */ |
|
if (rcv_ind_val < bta_ag_local_hf_ind_cfg[rcv_ind_id].ind_min_val || |
|
rcv_ind_val > bta_ag_local_hf_ind_cfg[rcv_ind_id].ind_max_val) { |
|
APPL_TRACE_WARNING("%s invalid ind_val %d", __func__, rcv_ind_val); |
|
return false; |
|
} |
|
|
|
val->lidx = rcv_ind_id; |
|
val->num = rcv_ind_val; |
|
|
|
return true; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_at_hfp_cback |
|
* |
|
* Description AT command processing callback for HFP. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_at_hfp_cback(tBTA_AG_SCB* p_scb, uint16_t cmd, uint8_t arg_type, |
|
char* p_arg, int16_t int_arg) { |
|
tBTA_AG_VAL val; |
|
tBTA_AG_SCB* ag_scb; |
|
uint32_t i, ind_id; |
|
uint32_t bia_masked_out; |
|
/** M: Bug fix for IOT Device. @{ */ |
|
static long hfp_conn_time = 0; |
|
/** @} */ |
|
|
|
if (p_arg == NULL) { |
|
APPL_TRACE_ERROR("%s: p_arg is null, send error and return", __func__); |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); |
|
return; |
|
} |
|
|
|
APPL_TRACE_DEBUG("%s: AT command %d, arg_type %d, int_arg %d, arg %s", |
|
__func__, cmd, arg_type, int_arg, p_arg); |
|
|
|
memset(&val, 0, sizeof(tBTA_AG_VAL)); |
|
val.hdr.handle = bta_ag_scb_to_idx(p_scb); |
|
val.hdr.app_id = p_scb->app_id; |
|
val.hdr.status = BTA_AG_SUCCESS; |
|
val.num = int_arg; |
|
val.bd_addr = p_scb->peer_addr; |
|
strlcpy(val.str, p_arg, sizeof(val.str)); |
|
|
|
/** |
|
* Unless this this is a local event, by default we'll forward |
|
* the event code to the application. |
|
* If |event| is 0 at the end of this function, the application |
|
* callback is NOT invoked. |
|
*/ |
|
tBTA_AG_EVT event = 0; |
|
if (cmd < BTA_AG_LOCAL_EVT_FIRST) event = cmd; |
|
|
|
switch (cmd) { |
|
case BTA_AG_AT_A_EVT: |
|
case BTA_AG_SPK_EVT: |
|
case BTA_AG_MIC_EVT: |
|
case BTA_AG_AT_CHUP_EVT: |
|
case BTA_AG_AT_CBC_EVT: |
|
/* send OK */ |
|
bta_ag_send_ok(p_scb); |
|
|
|
/** M: Ignore the immediate mute command. @{ */ |
|
// Some device send VGS/VGM = 0 when connected HFP, this |
|
// will cause no voice for call, so ignore the command in a while |
|
// after HFP connected. |
|
if ((cmd == BTA_AG_SPK_EVT ||cmd == BTA_AG_MIC_EVT) && val.num == 0) { |
|
long cur_time = time(NULL); |
|
APPL_TRACE_DEBUG("%s: cur_time = %ld, hfp_conn_time = %ld", __func__, cur_time, hfp_conn_time); |
|
if (cur_time - hfp_conn_time < 2) { |
|
event = 0; |
|
} |
|
} |
|
if (cmd == BTA_AG_AT_A_EVT) |
|
alarm_cancel(p_scb->ring_timer); |
|
/** @} */ |
|
break; |
|
|
|
case BTA_AG_AT_BLDN_EVT: |
|
/* Do not send OK, App will send error or OK depending on |
|
** last dial number enabled or not */ |
|
break; |
|
|
|
case BTA_AG_AT_D_EVT: |
|
/* Do not send OK for Dial cmds |
|
** Let application decide whether to send OK or ERROR*/ |
|
|
|
/* if mem dial cmd, make sure string contains only digits */ |
|
if (val.str[0] == '>') { |
|
/* Some car kits may add some unwanted space characters in the |
|
** input string. This workaround will trim the unwanted chars. */ |
|
remove_spaces(val.str + 1); |
|
|
|
if (!utl_isintstr(val.str + 1)) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); |
|
} |
|
} else if (val.str[0] == 'V') /* ATDV : Dial VoIP Call */ |
|
{ |
|
/* We do not check string. Code will be added later if needed. */ |
|
if (!((p_scb->peer_features & BTA_AG_PEER_FEAT_VOIP) && |
|
(p_scb->features & BTA_AG_FEAT_VOIP))) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
} |
|
/* If dial cmd, make sure string contains only dial digits |
|
** Dial digits are 0-9, A-C, *, #, + */ |
|
else { |
|
/* Some car kits may add some unwanted space characters in the |
|
** input string. This workaround will trim the unwanted chars. */ |
|
remove_spaces(val.str); |
|
|
|
if (!utl_isdialstr(val.str)) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR); |
|
} |
|
} |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_CCWA: |
|
/* store setting */ |
|
p_scb->ccwa_enabled = (bool)int_arg; |
|
|
|
/* send OK */ |
|
bta_ag_send_ok(p_scb); |
|
break; |
|
|
|
case BTA_AG_AT_CHLD_EVT: |
|
if (arg_type == BTA_AG_AT_TEST) { |
|
/* don't call callback */ |
|
event = 0; |
|
|
|
/* send CHLD string */ |
|
/* Form string based on supported 1.5 feature */ |
|
if ((p_scb->peer_version >= HFP_VERSION_1_5) && |
|
(p_scb->features & BTA_AG_FEAT_ECC) && |
|
(p_scb->peer_features & BTA_AG_PEER_FEAT_ECC)) |
|
bta_ag_send_result(p_scb, BTA_AG_IN_CALL_HELD_RES, |
|
p_bta_ag_cfg->chld_val_ecc, 0); |
|
else |
|
bta_ag_send_result(p_scb, BTA_AG_IN_CALL_HELD_RES, |
|
p_bta_ag_cfg->chld_val, 0); |
|
|
|
/* send OK */ |
|
bta_ag_send_ok(p_scb); |
|
|
|
/* if service level conn. not already open, now it's open */ |
|
bta_ag_svc_conn_open(p_scb, NULL); |
|
} else { |
|
val.idx = bta_ag_parse_chld(p_scb, val.str); |
|
|
|
if (val.idx == BTA_AG_INVALID_CHLD) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
break; |
|
} |
|
if (val.idx && |
|
!((p_scb->features & BTA_AG_FEAT_ECC) && |
|
(p_scb->peer_features & BTA_AG_PEER_FEAT_ECC))) { |
|
/* we do not support ECC, but HF is sending us a CHLD with call |
|
* index*/ |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
|
|
} else { |
|
/* If it is swap between calls, set call held indicator to 3(out of |
|
*valid 0-2) |
|
** Application will set it back to 1 |
|
** callheld indicator will be sent across to the peer. */ |
|
if (val.str[0] == '2') { |
|
for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; |
|
i++, ag_scb++) { |
|
if (ag_scb->in_use) { |
|
if ((ag_scb->call_ind == BTA_AG_CALL_ACTIVE) && |
|
(ag_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE)) |
|
ag_scb->callheld_ind = BTA_AG_CALLHELD_NOACTIVE + 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* Do not send OK. Let app decide after parsing the val str */ |
|
/* bta_ag_send_ok(p_scb); */ |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BIND_EVT: |
|
APPL_TRACE_DEBUG("%s BTA_AG_AT_BIND_EVT arg_type: %d", __func__, |
|
arg_type); |
|
if (arg_type == BTA_AG_AT_SET) { |
|
if (bta_ag_parse_bind_set(p_scb, val)) { |
|
bta_ag_send_ok(p_scb); |
|
} else { |
|
event = 0; /* don't call callback */ |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INVALID_INDEX); |
|
} |
|
} else { |
|
bta_ag_bind_response(p_scb, arg_type); |
|
|
|
/* Need not pass this command beyond BTIF.*/ |
|
/* Stack handles it internally */ |
|
event = 0; /* don't call callback */ |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BIEV_EVT: |
|
if (bta_ag_parse_biev_response(p_scb, &val)) { |
|
bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INVALID_INDEX); |
|
/* don't call callback receiving invalid indicator */ |
|
event = 0; |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_CIND_EVT: |
|
if (arg_type == BTA_AG_AT_TEST) { |
|
/* don't call callback */ |
|
event = 0; |
|
|
|
/* send CIND string, send OK */ |
|
bta_ag_send_result(p_scb, BTA_AG_CIND_RES, p_bta_ag_cfg->cind_info, 0); |
|
bta_ag_send_ok(p_scb); |
|
} |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_CLIP: |
|
/* store setting, send OK */ |
|
p_scb->clip_enabled = (bool)int_arg; |
|
bta_ag_send_ok(p_scb); |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_CMER: |
|
/* if parsed ok store setting, send OK */ |
|
if (bta_ag_parse_cmer(p_arg, &p_scb->cmer_enabled)) { |
|
bta_ag_send_ok(p_scb); |
|
|
|
/* if service level conn. not already open and our features and |
|
** peer features do not have 3-way, service level conn. now open |
|
*/ |
|
if (!p_scb->svc_conn && |
|
!((p_scb->features & BTA_AG_FEAT_3WAY) && |
|
(p_scb->peer_features & BTA_AG_PEER_FEAT_3WAY))) { |
|
bta_ag_svc_conn_open(p_scb, NULL); |
|
} |
|
} else { |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_VTS_EVT: |
|
/* check argument */ |
|
if (strlen(p_arg) == 1) { |
|
bta_ag_send_ok(p_scb); |
|
} else { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BINP_EVT: |
|
/* if feature not set don't call callback, send ERROR */ |
|
if (!(p_scb->features & BTA_AG_FEAT_VTAG)) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BVRA_EVT: |
|
/* if feature not supported don't call callback, send ERROR. App will send |
|
* OK */ |
|
if (!(p_scb->features & BTA_AG_FEAT_VREC)) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_BRSF: { |
|
/* store peer features */ |
|
p_scb->peer_features = (uint16_t)int_arg; |
|
|
|
/** M: Bug fix for IOT Device. @{ */ |
|
hfp_conn_time = time(NULL); |
|
/** @} */ |
|
|
|
tBTA_AG_FEAT features = p_scb->features; |
|
if (p_scb->peer_version < HFP_VERSION_1_7) { |
|
features &= HFP_1_6_FEAT_MASK; |
|
} |
|
/** M: Add for HFP 1.7 to 1.6 blacklist @{ */ |
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
else { |
|
features = check_hfp_ag_feature_by_blacklist(features, p_scb->peer_addr); |
|
} |
|
#endif |
|
/** @} */ |
|
|
|
/** M: New Feature for setting volume support to property. @{ */ |
|
if ((p_scb->peer_features & BTA_AG_PEER_FEAT_VOL) == 0) { |
|
property_set("persist.service.bdroid.vol", "0"); |
|
} else { |
|
property_set("persist.service.bdroid.vol", "1"); |
|
} |
|
/** @} */ |
|
|
|
APPL_TRACE_DEBUG("%s BRSF HF: 0x%x, phone: 0x%x", __func__, |
|
p_scb->peer_features, features); |
|
|
|
/* send BRSF, send OK */ |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_BRSF, NULL, (int16_t)features); |
|
bta_ag_send_ok(p_scb); |
|
break; |
|
} |
|
|
|
case BTA_AG_AT_NREC_EVT: |
|
/* if feature send OK, else don't call callback, send ERROR */ |
|
if (p_scb->features & BTA_AG_FEAT_ECNR) { |
|
bta_ag_send_ok(p_scb); |
|
} else { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BTRH_EVT: |
|
/* if feature send BTRH, send OK:, else don't call callback, send ERROR */ |
|
if (p_scb->features & BTA_AG_FEAT_BTRH) { |
|
/* If set command; send response and notify app */ |
|
if (arg_type == BTA_AG_AT_SET) { |
|
for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; |
|
i++, ag_scb++) { |
|
if (ag_scb->in_use) { |
|
bta_ag_send_result(ag_scb, BTA_AG_BTRH_RES, NULL, int_arg); |
|
} |
|
} |
|
bta_ag_send_ok(p_scb); |
|
} else /* Read Command */ |
|
{ |
|
val.num = BTA_AG_BTRH_READ; |
|
} |
|
} else { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_COPS_EVT: |
|
if (arg_type == BTA_AG_AT_SET) { |
|
/* don't call callback */ |
|
event = 0; |
|
|
|
/* send OK */ |
|
bta_ag_send_ok(p_scb); |
|
} |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_CMEE: |
|
if (p_scb->features & BTA_AG_FEAT_EXTERR) { |
|
/* store setting */ |
|
p_scb->cmee_enabled = (bool)int_arg; |
|
|
|
/* send OK */ |
|
bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
/* don't call callback */ |
|
event = 0; |
|
break; |
|
|
|
case BTA_AG_LOCAL_EVT_BIA: |
|
/* don't call callback */ |
|
event = 0; |
|
|
|
bia_masked_out = p_scb->bia_masked_out; |
|
|
|
/* Parse the indicator mask */ |
|
for (i = 0, ind_id = 1; (val.str[i] != 0) && (ind_id <= 20); |
|
i++, ind_id++) { |
|
if (val.str[i] == ',') continue; |
|
|
|
if (val.str[i] == '0') |
|
bia_masked_out |= ((uint32_t)1 << ind_id); |
|
else if (val.str[i] == '1') |
|
bia_masked_out &= ~((uint32_t)1 << ind_id); |
|
else |
|
break; |
|
|
|
i++; |
|
if ((val.str[i] == 0) || (val.str[i] != ',')) break; |
|
} |
|
if (val.str[i] == 0) { |
|
p_scb->bia_masked_out = bia_masked_out; |
|
bta_ag_send_ok(p_scb); |
|
} else |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INVALID_INDEX); |
|
break; |
|
|
|
case BTA_AG_AT_CNUM_EVT: |
|
break; |
|
|
|
case BTA_AG_AT_CLCC_EVT: |
|
if (!(p_scb->features & BTA_AG_FEAT_ECS)) { |
|
event = 0; |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BAC_EVT: |
|
bta_ag_send_ok(p_scb); |
|
|
|
/* store available codecs from the peer */ |
|
if ((p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC) && |
|
(p_scb->features & BTA_AG_FEAT_CODEC)) { |
|
p_scb->peer_codecs = bta_ag_parse_bac(p_scb, p_arg); |
|
p_scb->codec_updated = true; |
|
|
|
if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) { |
|
p_scb->sco_codec = UUID_CODEC_MSBC; |
|
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to MSBC"); |
|
} else { |
|
p_scb->sco_codec = UUID_CODEC_CVSD; |
|
APPL_TRACE_DEBUG("Received AT+BAC, updating sco codec to CVSD"); |
|
} |
|
/* The above logic sets the stack preferred codec based on local and |
|
peer codec |
|
capabilities. This can be overridden by the application depending on its |
|
preference |
|
using the bta_ag_setcodec API. We send the peer_codecs to the |
|
application. */ |
|
val.num = p_scb->peer_codecs; |
|
/* Received BAC while in codec negotiation. */ |
|
if ((bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST) && |
|
(bta_ag_cb.sco.p_curr_scb == p_scb)) { |
|
bta_ag_codec_negotiate(p_scb); |
|
} |
|
} else { |
|
p_scb->peer_codecs = BTA_AG_CODEC_CVSD; |
|
APPL_TRACE_ERROR( |
|
"Unexpected CMD:AT+BAC, Codec Negotiation is not supported"); |
|
} |
|
break; |
|
|
|
case BTA_AG_AT_BCS_EVT: { |
|
tBTA_AG_PEER_CODEC codec_type, codec_sent; |
|
bta_ag_send_ok(p_scb); |
|
alarm_cancel(p_scb->codec_negotiation_timer); |
|
|
|
switch (int_arg) { |
|
case UUID_CODEC_CVSD: |
|
codec_type = BTA_AG_CODEC_CVSD; |
|
break; |
|
case UUID_CODEC_MSBC: |
|
codec_type = BTA_AG_CODEC_MSBC; |
|
break; |
|
default: |
|
APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg); |
|
codec_type = 0xFFFF; |
|
break; |
|
} |
|
|
|
if (p_scb->codec_fallback) |
|
codec_sent = BTA_AG_CODEC_CVSD; |
|
else |
|
codec_sent = p_scb->sco_codec; |
|
|
|
if (codec_type == codec_sent) |
|
bta_ag_sco_codec_nego(p_scb, true); |
|
else |
|
bta_ag_sco_codec_nego(p_scb, false); |
|
|
|
/* send final codec info to callback */ |
|
val.num = codec_sent; |
|
break; |
|
} |
|
case BTA_AG_LOCAL_EVT_BCC: |
|
bta_ag_send_ok(p_scb); |
|
bta_ag_sco_open(p_scb, NULL); |
|
break; |
|
|
|
default: |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
break; |
|
} |
|
|
|
/* call callback */ |
|
if (event != 0) { |
|
(*bta_ag_cb.p_cback)(event, (tBTA_AG*)&val); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_at_err_cback |
|
* |
|
* Description AT command parser error callback. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_at_err_cback(tBTA_AG_SCB* p_scb, bool unknown, char* p_arg) { |
|
tBTA_AG_VAL val; |
|
|
|
if (unknown && (!strlen(p_arg))) { |
|
APPL_TRACE_DEBUG("Empty AT cmd string received"); |
|
bta_ag_send_ok(p_scb); |
|
return; |
|
} |
|
|
|
/* if unknown AT command and configured to pass these to app */ |
|
if (unknown && (p_scb->features & BTA_AG_FEAT_UNAT)) { |
|
val.hdr.handle = bta_ag_scb_to_idx(p_scb); |
|
val.hdr.app_id = p_scb->app_id; |
|
val.hdr.status = BTA_AG_SUCCESS; |
|
val.num = 0; |
|
strlcpy(val.str, p_arg, sizeof(val.str)); |
|
(*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG*)&val); |
|
} else { |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_hsp_result |
|
* |
|
* Description Handle API result for HSP connections. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_hsp_result(tBTA_AG_SCB* p_scb, tBTA_AG_API_RESULT* p_result) { |
|
APPL_TRACE_DEBUG("bta_ag_hsp_result : res = %d", p_result->result); |
|
|
|
switch (p_result->result) { |
|
case BTA_AG_SPK_RES: |
|
case BTA_AG_MIC_RES: |
|
bta_ag_send_result(p_scb, p_result->result, NULL, p_result->data.num); |
|
break; |
|
|
|
case BTA_AG_IN_CALL_RES: |
|
/* tell sys to stop av if any */ |
|
bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
|
|
/* if sco already opened or no inband ring send ring now */ |
|
if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || |
|
(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
bta_ag_send_ring(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else { |
|
/* else open sco, send ring after sco opened */ |
|
/* HSPv1.2: AG shall not send RING if using in-band ring tone. */ |
|
if (p_scb->peer_version >= HSP_VERSION_1_2) { |
|
p_scb->post_sco = BTA_AG_POST_SCO_NONE; |
|
} else { |
|
p_scb->post_sco = BTA_AG_POST_SCO_RING; |
|
} |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
break; |
|
|
|
case BTA_AG_IN_CALL_CONN_RES: |
|
case BTA_AG_OUT_CALL_ORIG_RES: |
|
/* if incoming call connected stop ring timer */ |
|
if (p_result->result == BTA_AG_IN_CALL_CONN_RES) { |
|
alarm_cancel(p_scb->ring_timer); |
|
} |
|
|
|
if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
/* if audio connected to this scb AND sco is not opened, open sco */ |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && |
|
!bta_ag_sco_is_open(p_scb)) { |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
/* else if no audio at call close sco */ |
|
else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE && |
|
bta_ag_sco_is_open(p_scb)) { |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
} |
|
break; |
|
|
|
case BTA_AG_END_CALL_RES: |
|
alarm_cancel(p_scb->ring_timer); |
|
|
|
/* close sco */ |
|
if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && |
|
!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else { |
|
/* if av got suspended by this call, let it resume. */ |
|
bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
} |
|
break; |
|
|
|
case BTA_AG_INBAND_RING_RES: |
|
p_scb->inband_enabled = p_result->data.state; |
|
APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); |
|
break; |
|
|
|
case BTA_AG_UNAT_RES: |
|
if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { |
|
if (p_result->data.str[0] != 0) { |
|
bta_ag_send_result(p_scb, p_result->result, p_result->data.str, 0); |
|
} |
|
|
|
if (p_result->data.ok_flag == BTA_AG_OK_DONE) bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR); |
|
} |
|
break; |
|
|
|
default: |
|
/* ignore all others */ |
|
break; |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_hfp_result |
|
* |
|
* Description Handle API result for HFP connections. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_hfp_result(tBTA_AG_SCB* p_scb, tBTA_AG_API_RESULT* p_result) { |
|
APPL_TRACE_DEBUG("bta_ag_hfp_result : res = %d", p_result->result); |
|
|
|
switch (p_result->result) { |
|
case BTA_AG_SPK_RES: |
|
case BTA_AG_MIC_RES: |
|
bta_ag_send_result(p_scb, p_result->result, NULL, p_result->data.num); |
|
break; |
|
|
|
case BTA_AG_IN_CALL_RES: |
|
/* tell sys to stop av if any */ |
|
bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
|
|
/* store caller id string. |
|
* append type info at the end. |
|
* make sure a valid type info is passed. |
|
* otherwise add 129 as default type */ |
|
if ((p_result->data.num < BTA_AG_CLIP_TYPE_MIN) || |
|
(p_result->data.num > BTA_AG_CLIP_TYPE_MAX)) { |
|
if (p_result->data.num != BTA_AG_CLIP_TYPE_VOIP) |
|
p_result->data.num = BTA_AG_CLIP_TYPE_DEFAULT; |
|
} |
|
|
|
APPL_TRACE_DEBUG("CLIP type :%d", p_result->data.num); |
|
p_scb->clip[0] = 0; |
|
if (p_result->data.str[0] != 0) |
|
snprintf(p_scb->clip, sizeof(p_scb->clip), "%s,%d", p_result->data.str, |
|
p_result->data.num); |
|
|
|
/* send callsetup indicator */ |
|
if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END) { |
|
/* Need to sent 2 callsetup IND's(Call End and Incoming call) after SCO |
|
* close. */ |
|
p_scb->post_sco = BTA_AG_POST_SCO_CALL_END_INCALL; |
|
} else { |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
|
|
/* if sco already opened or no inband ring send ring now */ |
|
if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) || |
|
(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
bta_ag_send_ring(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else { |
|
/* else open sco, send ring after sco opened */ |
|
p_scb->post_sco = BTA_AG_POST_SCO_RING; |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
} |
|
break; |
|
|
|
case BTA_AG_IN_CALL_CONN_RES: |
|
alarm_cancel(p_scb->ring_timer); |
|
|
|
/* if sco not opened and we need to open it, send indicators first |
|
** then open sco. |
|
*/ |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
|
|
if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && |
|
!bta_ag_sco_is_open(p_scb)) { |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else if ((p_result->data.audio_handle == BTA_AG_HANDLE_NONE) && |
|
bta_ag_sco_is_open(p_scb)) { |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
} |
|
break; |
|
|
|
case BTA_AG_IN_CALL_HELD_RES: |
|
alarm_cancel(p_scb->ring_timer); |
|
|
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
|
|
break; |
|
|
|
case BTA_AG_OUT_CALL_ORIG_RES: |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && |
|
!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
break; |
|
|
|
case BTA_AG_OUT_CALL_ALERT_RES: |
|
/* send indicators */ |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) && |
|
!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
break; |
|
|
|
case BTA_AG_MULTI_CALL_RES: |
|
/* open SCO at SLC for this three way call */ |
|
APPL_TRACE_DEBUG("Headset Connected in three way call"); |
|
if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
break; |
|
|
|
case BTA_AG_OUT_CALL_CONN_RES: |
|
/* send indicators */ |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
|
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
/** M: bug fix for end call display on carkit. 600ms is ok test result for the carkit @{ */ |
|
if (interop_mtk_match_addr_name(INTEROP_MTK_HFP_DELAY_SCO_IN_ACT, |
|
&(p_scb->peer_addr))) { |
|
APPL_TRACE_DEBUG("Dealy setup sco after call active"); |
|
usleep(600 * 1000); |
|
} |
|
/** @} */ |
|
#endif |
|
|
|
/* open or close sco */ |
|
if (!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb)) { |
|
bta_ag_sco_open(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE) { |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} |
|
} |
|
break; |
|
|
|
case BTA_AG_CALL_CANCEL_RES: |
|
/** M: bug fix for ring timer not cancel. @{ */ |
|
alarm_cancel(p_scb->ring_timer); |
|
/** @} */ |
|
/* send indicators */ |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
break; |
|
|
|
case BTA_AG_END_CALL_RES: |
|
alarm_cancel(p_scb->ring_timer); |
|
|
|
/* if sco open, close sco then send indicator values */ |
|
if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && |
|
!(p_scb->features & BTA_AG_FEAT_NOSCO)) { |
|
p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; |
|
bta_ag_sco_close(p_scb, (tBTA_AG_DATA*)p_result); |
|
} else if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END_INCALL) { |
|
/* sco closing for outgoing call because of incoming call */ |
|
/* Send only callsetup end indicator after sco close */ |
|
p_scb->post_sco = BTA_AG_POST_SCO_CALL_END; |
|
} else { |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
|
|
/* if av got suspended by this call, let it resume. */ |
|
bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
} |
|
break; |
|
|
|
case BTA_AG_INBAND_RING_RES: |
|
p_scb->inband_enabled = p_result->data.state; |
|
APPL_TRACE_DEBUG("inband_enabled set to %d", p_scb->inband_enabled); |
|
bta_ag_send_result(p_scb, p_result->result, NULL, p_result->data.state); |
|
break; |
|
|
|
case BTA_AG_CIND_RES: |
|
/* store local values */ |
|
p_scb->call_ind = p_result->data.str[0] - '0'; |
|
p_scb->callsetup_ind = p_result->data.str[2] - '0'; |
|
p_scb->service_ind = p_result->data.str[4] - '0'; |
|
p_scb->signal_ind = p_result->data.str[6] - '0'; |
|
p_scb->roam_ind = p_result->data.str[8] - '0'; |
|
p_scb->battchg_ind = p_result->data.str[10] - '0'; |
|
p_scb->callheld_ind = p_result->data.str[12] - '0'; |
|
APPL_TRACE_DEBUG("cind call:%d callsetup:%d", p_scb->call_ind, |
|
p_scb->callsetup_ind); |
|
|
|
bta_ag_send_result(p_scb, p_result->result, p_result->data.str, 0); |
|
bta_ag_send_ok(p_scb); |
|
break; |
|
|
|
case BTA_AG_BINP_RES: |
|
case BTA_AG_CNUM_RES: |
|
case BTA_AG_CLCC_RES: |
|
case BTA_AG_COPS_RES: |
|
if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { |
|
if (p_result->data.str[0] != 0) { |
|
bta_ag_send_result(p_scb, p_result->result, p_result->data.str, 0); |
|
} |
|
|
|
if (p_result->data.ok_flag == BTA_AG_OK_DONE) bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, p_result->data.errcode); |
|
} |
|
break; |
|
|
|
case BTA_AG_UNAT_RES: |
|
if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { |
|
if (p_result->data.str[0] != 0) { |
|
bta_ag_process_unat_res(p_result->data.str); |
|
APPL_TRACE_DEBUG("BTA_AG_RES :%s", p_result->data.str); |
|
bta_ag_send_result(p_scb, p_result->result, p_result->data.str, 0); |
|
} |
|
|
|
if (p_result->data.ok_flag == BTA_AG_OK_DONE) bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, p_result->data.errcode); |
|
} |
|
break; |
|
|
|
case BTA_AG_CALL_WAIT_RES: |
|
if (p_scb->ccwa_enabled) { |
|
bta_ag_send_result(p_scb, p_result->result, p_result->data.str, 0); |
|
} |
|
bta_ag_send_call_inds(p_scb, p_result->result); |
|
break; |
|
|
|
case BTA_AG_IND_RES: |
|
bta_ag_send_ind(p_scb, p_result->data.ind.id, p_result->data.ind.value, |
|
false); |
|
break; |
|
|
|
case BTA_AG_BVRA_RES: |
|
bta_ag_send_result(p_scb, p_result->result, NULL, p_result->data.state); |
|
break; |
|
|
|
case BTA_AG_BTRH_RES: |
|
if (p_result->data.ok_flag != BTA_AG_OK_ERROR) { |
|
/* Don't respond to read if not in response & hold state */ |
|
if (p_result->data.num != BTA_AG_BTRH_NO_RESP) { |
|
bta_ag_send_result(p_scb, p_result->result, NULL, p_result->data.num); |
|
} |
|
|
|
/* In case of a response to a read request we need to send OK */ |
|
if (p_result->data.ok_flag == BTA_AG_OK_DONE) bta_ag_send_ok(p_scb); |
|
} else { |
|
bta_ag_send_error(p_scb, p_result->data.errcode); |
|
} |
|
break; |
|
|
|
case BTA_AG_BIND_RES: { |
|
/* Find whether ind_id is supported by local device or not */ |
|
int local_index = bta_ag_find_hf_ind_by_id(p_scb->local_hf_indicators, |
|
BTA_AG_MAX_NUM_LOCAL_HF_IND, |
|
p_result->data.ind.id); |
|
if (local_index == -1) { |
|
APPL_TRACE_WARNING("%s Invalid HF Indicator ID %d", __func__, |
|
p_result->data.ind.id); |
|
return; |
|
} |
|
|
|
/* Find whether ind_id is supported by peer device or not */ |
|
int peer_index = bta_ag_find_hf_ind_by_id(p_scb->peer_hf_indicators, |
|
BTA_AG_MAX_NUM_PEER_HF_IND, |
|
p_result->data.ind.id); |
|
if (peer_index == -1) { |
|
APPL_TRACE_WARNING("%s Invalid HF Indicator ID %d", __func__, |
|
p_result->data.ind.id); |
|
return; |
|
} else { |
|
/* If the current state is different from the one upper layer request |
|
change current state and send out the result */ |
|
if (p_scb->local_hf_indicators[local_index].is_enable != |
|
p_result->data.ind.on_demand) { |
|
char buffer[BTA_AG_AT_MAX_LEN] = {0}; |
|
char* p = buffer; |
|
|
|
p_scb->local_hf_indicators[local_index].is_enable = |
|
p_result->data.ind.on_demand; |
|
p += utl_itoa(p_result->data.ind.id, p); |
|
*p++ = ','; |
|
p += utl_itoa(p_scb->local_hf_indicators[local_index].is_enable, p); |
|
|
|
bta_ag_send_result(p_scb, p_result->result, buffer, 0); |
|
} else { |
|
APPL_TRACE_DEBUG( |
|
"%s HF Indicator %d already %s", p_result->data.ind.id, |
|
(p_result->data.ind.on_demand == true) ? "Enabled" : "Disabled"); |
|
} |
|
} |
|
break; |
|
} |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_result |
|
* |
|
* Description Handle API result. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_result(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) { |
|
if (p_scb->conn_service == BTA_AG_HSP) { |
|
bta_ag_hsp_result(p_scb, &p_data->api_result); |
|
} else { |
|
bta_ag_hfp_result(p_scb, &p_data->api_result); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_bcs |
|
* |
|
* Description Send +BCS AT command to peer. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_send_bcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) { |
|
uint16_t codec_uuid; |
|
|
|
if (p_scb->codec_fallback) { |
|
codec_uuid = UUID_CODEC_CVSD; |
|
} else { |
|
switch (p_scb->sco_codec) { |
|
case BTA_AG_CODEC_NONE: |
|
codec_uuid = UUID_CODEC_CVSD; |
|
break; |
|
case BTA_AG_CODEC_CVSD: |
|
codec_uuid = UUID_CODEC_CVSD; |
|
break; |
|
case BTA_AG_CODEC_MSBC: |
|
codec_uuid = UUID_CODEC_MSBC; |
|
break; |
|
default: |
|
APPL_TRACE_ERROR("bta_ag_send_bcs: unknown codec %d, use CVSD", |
|
p_scb->sco_codec); |
|
codec_uuid = UUID_CODEC_CVSD; |
|
break; |
|
} |
|
} |
|
|
|
/* send +BCS */ |
|
APPL_TRACE_DEBUG("send +BCS codec is %d", codec_uuid); |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_BCS, NULL, codec_uuid); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_send_ring |
|
* |
|
* Description Send RING result code to peer. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_send_ring(tBTA_AG_SCB* p_scb, UNUSED_ATTR tBTA_AG_DATA* p_data) { |
|
/* send RING */ |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_RING, NULL, 0); |
|
|
|
/* if HFP and clip enabled and clip data send CLIP */ |
|
if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && |
|
p_scb->clip[0] != 0) { |
|
bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_CLIP, p_scb->clip, 0); |
|
} |
|
|
|
bta_sys_start_timer(p_scb->ring_timer, BTA_AG_RING_TIMEOUT_MS, |
|
BTA_AG_RING_TIMEOUT_EVT, bta_ag_scb_to_idx(p_scb)); |
|
}
|
|
|