/******************************************************************************
 *
 *  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.
 *
 ******************************************************************************/

#include <string.h>
#include <stdlib.h>
#include "common/bt_defs.h"
#include "bta/bta_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_hf_client_api.h"
#include "bta_hf_client_int.h"

#if (BTA_HF_INCLUDED == TRUE)
/* uncomment to enable extra debug */
/* #define BTA_HF_CLIENT_DEBUG TRUE */

#ifndef BTA_HF_CLIENT_DEBUG
#define BTA_HF_CLIENT_DEBUG FALSE
#endif

#if BTA_HF_CLIENT_DEBUG == TRUE
static char *bta_hf_client_evt_str(UINT16 event);
static char *bta_hf_client_state_str(UINT8 state);
#endif

/* state machine states */
enum {
    BTA_HF_CLIENT_INIT_ST,
    BTA_HF_CLIENT_OPENING_ST,
    BTA_HF_CLIENT_OPEN_ST,
    BTA_HF_CLIENT_CLOSING_ST
};

/* state machine action enumeration list */
enum {
    BTA_HF_CLIENT_REGISTER,
    BTA_HF_CLIENT_DEREGISTER,
    BTA_HF_CLIENT_START_DEREG,
    BTA_HF_CLIENT_RFC_DO_CLOSE,
    BTA_HF_CLIENT_START_CLOSE,
    BTA_HF_CLIENT_START_OPEN,
    BTA_HF_CLIENT_RFC_ACP_OPEN,
    BTA_HF_CLIENT_SCO_LISTEN,
    BTA_HF_CLIENT_SCO_CONN_OPEN,
    BTA_HF_CLIENT_SCO_CONN_CLOSE,
    BTA_HF_CLIENT_SCO_OPEN,
    BTA_HF_CLIENT_SCO_CLOSE,
    BTA_HF_CLIENT_SCO_SHUTDOWN,
    BTA_HF_CLIENT_FREE_DB,
    BTA_HF_CLIENT_OPEN_FAIL,
    BTA_HF_CLIENT_RFC_OPEN,
    BTA_HF_CLIENT_RFC_FAIL,
    BTA_HF_CLIENT_DISC_INT_RES,
    BTA_HF_CLIENT_RFC_DO_OPEN,
    BTA_HF_CLIENT_DISC_FAIL,
    BTA_HF_CLIENT_RFC_CLOSE,
    BTA_HF_CLIENT_RFC_DATA,
    BTA_HF_CLIENT_DISC_ACP_RES,
    BTA_HF_CLIENT_SVC_CONN_OPEN,
    BTA_HF_CLIENT_SEND_AT_CMD,
#if (BTM_SCO_HCI_INCLUDED == TRUE)
    BTA_HF_CLIENT_CI_SCO_DATA,
#endif
    BTA_HF_CLIENT_NUM_ACTIONS,
};

#define BTA_HF_CLIENT_IGNORE BTA_HF_CLIENT_NUM_ACTIONS

/* type for action functions */
typedef void (*tBTA_HF_CLIENT_ACTION)(tBTA_HF_CLIENT_DATA *p_data);

/* action functions table, indexed with action enum */
const tBTA_HF_CLIENT_ACTION bta_hf_client_action[] = {
    /* BTA_HF_CLIENT_REGISTER */      bta_hf_client_register,
    /* BTA_HF_CLIENT_DEREGISTER */    bta_hf_client_deregister,
    /* BTA_HF_CLIENT_START_DEREG */   bta_hf_client_start_dereg,
    /* BTA_HF_CLIENT_RFC_DO_CLOSE */  bta_hf_client_rfc_do_close,
    /* BTA_HF_CLIENT_START_CLOSE */   bta_hf_client_start_close,
    /* BTA_HF_CLIENT_START_OPEN */    bta_hf_client_start_open,
    /* BTA_HF_CLIENT_RFC_ACP_OPEN */  bta_hf_client_rfc_acp_open,
    /* BTA_HF_CLIENT_SCO_LISTEN */    bta_hf_client_sco_listen,
    /* BTA_HF_CLIENT_SCO_CONN_OPEN */ bta_hf_client_sco_conn_open,
    /* BTA_HF_CLIENT_SCO_CONN_CLOSE*/ bta_hf_client_sco_conn_close,
    /* BTA_HF_CLIENT_SCO_OPEN */      bta_hf_client_sco_open,
    /* BTA_HF_CLIENT_SCO_CLOSE */     bta_hf_client_sco_close,
    /* BTA_HF_CLIENT_SCO_SHUTDOWN */  bta_hf_client_sco_shutdown,
    /* BTA_HF_CLIENT_FREE_DB */       bta_hf_client_free_db,
    /* BTA_HF_CLIENT_OPEN_FAIL */     bta_hf_client_open_fail,
    /* BTA_HF_CLIENT_RFC_OPEN */      bta_hf_client_rfc_open,
    /* BTA_HF_CLIENT_RFC_FAIL */      bta_hf_client_rfc_fail,
    /* BTA_HF_CLIENT_DISC_INT_RES */  bta_hf_client_disc_int_res,
    /* BTA_HF_CLIENT_RFC_DO_OPEN */   bta_hf_client_rfc_do_open,
    /* BTA_HF_CLIENT_DISC_FAIL */     bta_hf_client_disc_fail,
    /* BTA_HF_CLIENT_RFC_CLOSE */     bta_hf_client_rfc_close,
    /* BTA_HF_CLIENT_RFC_DATA */      bta_hf_client_rfc_data,
    /* BTA_HF_CLIENT_DISC_ACP_RES */  bta_hf_client_disc_acp_res,
    /* BTA_HF_CLIENT_SVC_CONN_OPEN */ bta_hf_client_svc_conn_open,
    /* BTA_HF_CLIENT_SEND_AT_CMD */   bta_hf_client_send_at_cmd,
#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* BTA_HF_CLIENT_CI_SCO_DATA */   bta_hf_client_ci_sco_data,
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};

/* state table information */
#define BTA_HF_CLIENT_ACTIONS              2       /* number of actions */
#define BTA_HF_CLIENT_NEXT_STATE           2       /* position of next state */
#define BTA_HF_CLIENT_NUM_COLS             3       /* number of columns in state tables */

/* state table for init state */
const UINT8 bta_hf_client_st_init[][BTA_HF_CLIENT_NUM_COLS] = {
    /* Event                    Action 1                       Action 2                       Next state */
    /* API_REGISTER_EVT */      {BTA_HF_CLIENT_REGISTER,       BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* API_DEREGISTER_EVT */    {BTA_HF_CLIENT_DEREGISTER,     BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* API_OPEN_EVT */          {BTA_HF_CLIENT_START_OPEN,     BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* API_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* API_AUDIO_OPEN_EVT */    {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* API_AUDIO_CLOSE_EVT */   {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_OPEN_EVT */          {BTA_HF_CLIENT_RFC_ACP_OPEN,   BTA_HF_CLIENT_SCO_LISTEN,      BTA_HF_CLIENT_OPEN_ST},
    /* RFC_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_SRV_CLOSE_EVT */     {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_DATA_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* DISC_ACP_RES_EVT */      {BTA_HF_CLIENT_FREE_DB,        BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* DISC_INT_RES_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* DISC_OK_EVT */           {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* DISC_FAIL_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* SCO_OPEN_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* SCO_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* SEND_AT_CMD_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* CI_SCO_DATA_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};

/* state table for opening state */
const UINT8 bta_hf_client_st_opening[][BTA_HF_CLIENT_NUM_COLS] = {
    /* Event                    Action 1                       Action 2                       Next state */
    /* API_REGISTER_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* API_DEREGISTER_EVT */    {BTA_HF_CLIENT_RFC_DO_CLOSE,   BTA_HF_CLIENT_START_DEREG,     BTA_HF_CLIENT_CLOSING_ST},
    /* API_OPEN_EVT */          {BTA_HF_CLIENT_OPEN_FAIL,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* API_CLOSE_EVT */         {BTA_HF_CLIENT_RFC_DO_CLOSE,   BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_AUDIO_OPEN_EVT */    {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* API_AUDIO_CLOSE_EVT */   {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* RFC_OPEN_EVT */          {BTA_HF_CLIENT_RFC_OPEN,       BTA_HF_CLIENT_SCO_LISTEN,      BTA_HF_CLIENT_OPEN_ST},
    /* RFC_CLOSE_EVT */         {BTA_HF_CLIENT_RFC_FAIL,       BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_SRV_CLOSE_EVT */     {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* RFC_DATA_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* DISC_ACP_RES_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* DISC_INT_RES_EVT */      {BTA_HF_CLIENT_DISC_INT_RES,   BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* DISC_OK_EVT */           {BTA_HF_CLIENT_RFC_DO_OPEN,    BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* DISC_FAIL_EVT */         {BTA_HF_CLIENT_DISC_FAIL,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* SCO_OPEN_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* SCO_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
    /* SEND_AT_CMD_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* CI_SCO_DATA_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPENING_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};

/* state table for open state */
const UINT8 bta_hf_client_st_open[][BTA_HF_CLIENT_NUM_COLS] = {
    /* Event                    Action 1                       Action 2                       Next state */
    /* API_REGISTER_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* API_DEREGISTER_EVT */    {BTA_HF_CLIENT_START_CLOSE,    BTA_HF_CLIENT_START_DEREG,     BTA_HF_CLIENT_CLOSING_ST},
    /* API_OPEN_EVT */          {BTA_HF_CLIENT_OPEN_FAIL,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* API_CLOSE_EVT */         {BTA_HF_CLIENT_START_CLOSE,    BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_AUDIO_OPEN_EVT */    {BTA_HF_CLIENT_SCO_OPEN,       BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* API_AUDIO_CLOSE_EVT */   {BTA_HF_CLIENT_SCO_CLOSE,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* RFC_OPEN_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* RFC_CLOSE_EVT */         {BTA_HF_CLIENT_RFC_CLOSE,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_SRV_CLOSE_EVT */     {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* RFC_DATA_EVT */          {BTA_HF_CLIENT_RFC_DATA,       BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* DISC_ACP_RES_EVT */      {BTA_HF_CLIENT_DISC_ACP_RES,   BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* DISC_INT_RES_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* DISC_OK_EVT */           {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* DISC_FAIL_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* SCO_OPEN_EVT */          {BTA_HF_CLIENT_SCO_CONN_OPEN,  BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* SCO_CLOSE_EVT */         {BTA_HF_CLIENT_SCO_CONN_CLOSE, BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
    /* SEND_AT_CMD_EVT */       {BTA_HF_CLIENT_SEND_AT_CMD,    BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* CI_SCO_DATA_EVT */       {BTA_HF_CLIENT_CI_SCO_DATA,    BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_OPEN_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};

/* state table for closing state */
const UINT8 bta_hf_client_st_closing[][BTA_HF_CLIENT_NUM_COLS] = {
    /* Event                    Action 1                       Action 2                       Next state */
    /* API_REGISTER_EVT */      {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_DEREGISTER_EVT */    {BTA_HF_CLIENT_START_DEREG,    BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_OPEN_EVT */          {BTA_HF_CLIENT_OPEN_FAIL,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_AUDIO_OPEN_EVT */    {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* API_AUDIO_CLOSE_EVT */   {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* RFC_OPEN_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* RFC_CLOSE_EVT */         {BTA_HF_CLIENT_RFC_CLOSE,      BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* RFC_SRV_CLOSE_EVT */     {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* RFC_DATA_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* DISC_ACP_RES_EVT */      {BTA_HF_CLIENT_FREE_DB,        BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* DISC_INT_RES_EVT */      {BTA_HF_CLIENT_FREE_DB,        BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_INIT_ST},
    /* DISC_OK_EVT */           {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* DISC_FAIL_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* SCO_OPEN_EVT */          {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* SCO_CLOSE_EVT */         {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
    /* SEND_AT_CMD_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* CI_SCO_DATA_EVT */       {BTA_HF_CLIENT_IGNORE,         BTA_HF_CLIENT_IGNORE,          BTA_HF_CLIENT_CLOSING_ST},
#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */
};

/* type for state table */
typedef const UINT8 (*tBTA_HF_CLIENT_ST_TBL)[BTA_HF_CLIENT_NUM_COLS];

/* state table */
const tBTA_HF_CLIENT_ST_TBL bta_hf_client_st_tbl[] = {
    bta_hf_client_st_init,
    bta_hf_client_st_opening,
    bta_hf_client_st_open,
    bta_hf_client_st_closing
};

/* HF Client control block */
#if BTA_DYNAMIC_MEMORY == FALSE
tBTA_HF_CLIENT_CB  bta_hf_client_cb;
#else
tBTA_HF_CLIENT_CB  *bta_hf_client_cb_ptr;
#endif


/*******************************************************************************
**
** Function         bta_hf_client_scb_init
**
** Description      Initialize an HF_Client service control block.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_scb_init(void)
{
    APPL_TRACE_DEBUG("%s", __FUNCTION__);

    memset(&bta_hf_client_cb.scb, 0, sizeof(tBTA_HF_CLIENT_SCB));
    bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX;
    bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
}

/*******************************************************************************
**
** Function         bta_hf_client_scb_disable
**
** Description      Disable a service control block.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_scb_disable(void)
{
    APPL_TRACE_DEBUG("%s", __FUNCTION__);

    bta_hf_client_scb_init();

    (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_DISABLE_EVT, NULL);
}

/*******************************************************************************
**
** Function         bta_hf_client_resume_open
**
** Description      Resume opening process.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_resume_open (void)
{
    APPL_TRACE_DEBUG ("%s", __FUNCTION__);

    /* resume opening process.  */
    if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_INIT_ST) {
        bta_hf_client_cb.scb.state = BTA_HF_CLIENT_OPENING_ST;
        bta_hf_client_start_open (NULL);
    }
}

/*******************************************************************************
**
** Function         bta_hf_client_colli_timer_cback
**
** Description      HF Client connection collision timer callback
**
**
** Returns          void
**
*******************************************************************************/
static void bta_hf_client_colli_timer_cback (TIMER_LIST_ENT *p_tle)
{
    APPL_TRACE_DEBUG("%s", __FUNCTION__);

    if (p_tle) {
        bta_hf_client_cb.scb.colli_tmr_on = FALSE;

        /* If the peer haven't opened connection, restart opening process */
        bta_hf_client_resume_open ();
    }
}

/*******************************************************************************
**
** Function         bta_hf_client_collision_cback
**
** Description      Get notified about collision.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id,
                                    UINT8 app_id, BD_ADDR peer_addr)
{
    UNUSED(status);
    UNUSED(app_id);
    UNUSED(peer_addr);

    if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_OPENING_ST) {
        if (id == BTA_ID_SYS) { /* ACL collision */
            APPL_TRACE_WARNING ("HF Client found collision (ACL) ...");
        } else if (id == BTA_ID_HS) { /* RFCOMM collision */
            APPL_TRACE_WARNING ("HF Client found collision (RFCOMM) ...");
        } else {
            APPL_TRACE_WARNING ("HF Client found collision (\?\?\?) ...");
        }

        bta_hf_client_cb.scb.state = BTA_HF_CLIENT_INIT_ST;

        /* Cancel SDP if it had been started. */
        if (bta_hf_client_cb.scb.p_disc_db) {
            (void)SDP_CancelServiceSearch (bta_hf_client_cb.scb.p_disc_db);
            bta_hf_client_free_db(NULL);
        }

        /* reopen registered server */
        /* Collision may be detected before or after we close servers. */
        bta_hf_client_start_server();

        /* Start timer to handle connection opening restart */
        bta_hf_client_cb.scb.colli_timer.p_cback = (TIMER_CBACK *)&bta_hf_client_colli_timer_cback;
        bta_sys_start_timer(&bta_hf_client_cb.scb.colli_timer, 0, BTA_HF_CLIENT_COLLISION_TIMER);
        bta_hf_client_cb.scb.colli_tmr_on = TRUE;
    }
}

/*******************************************************************************
**
** Function         bta_hf_client_api_enable
**
** Description      Handle an API enable event.
**
**
** Returns          void
**
*******************************************************************************/
static void bta_hf_client_api_enable(tBTA_HF_CLIENT_DATA *p_data)
{
    /* initialize control block */
    memset(&bta_hf_client_cb, 0, sizeof(tBTA_HF_CLIENT_CB));

    /* store callback function */
    bta_hf_client_cb.p_cback = p_data->api_enable.p_cback;

    /* check if mSBC support enabled */
#if 0 // todo
    char value[PROPERTY_VALUE_MAX];
    property_get("ro.bluetooth.hfp.ver", value, "0");
    if (strcmp(value, "1.6") == 0) {
        bta_hf_client_cb.msbc_enabled = TRUE;
    }
#else
    bta_hf_client_cb.msbc_enabled = FALSE;
#endif

    bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;

    /* set same setting as AG does */
    BTM_WriteVoiceSettings(AG_VOICE_SETTINGS);

    bta_sys_collision_register (BTA_ID_HS, bta_hf_client_collision_cback);

    /* call callback with enable event */
    (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_ENABLE_EVT, NULL);
}

/*******************************************************************************
**
** Function         bta_hf_client_api_disable
**
** Description      Handle an API disable event.
**
**
** Returns          void
**
*******************************************************************************/
static void bta_hf_client_api_disable(tBTA_HF_CLIENT_DATA *p_data)
{
    if (!bta_sys_is_register (BTA_ID_HS)) {
        APPL_TRACE_ERROR("BTA HF Client is already disabled, ignoring ...");
        return;
    }

    /* De-register with BTA system manager */
    bta_sys_deregister(BTA_ID_HS);

    bta_hf_client_sm_execute(BTA_HF_CLIENT_API_DEREGISTER_EVT, p_data);

    bta_sys_collision_register (BTA_ID_HS, NULL);
}

/*******************************************************************************
**
** Function         bta_hf_client_hdl_event
**
** Description      Data HF Client main event handling function.
**
**
** Returns          BOOLEAN
**
*******************************************************************************/
BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg)
{
#if BTA_HF_CLIENT_DEBUG == TRUE
    APPL_TRACE_DEBUG("bta_hf_client_hdl_event %s (0x%x)", bta_hf_client_evt_str(p_msg->event), p_msg->event);
#endif

    switch (p_msg->event) {
    /* handle enable event */
    case BTA_HF_CLIENT_API_ENABLE_EVT:
        bta_hf_client_api_enable((tBTA_HF_CLIENT_DATA *) p_msg);
        break;

    /* handle disable event */
    case BTA_HF_CLIENT_API_DISABLE_EVT:
        bta_hf_client_api_disable((tBTA_HF_CLIENT_DATA *) p_msg);
        break;

    default:
        bta_hf_client_sm_execute(p_msg->event, (tBTA_HF_CLIENT_DATA *) p_msg);
        break;
    }
    return TRUE;
}

/*******************************************************************************
**
** Function         bta_hf_client_sm_execute
**
** Description      State machine event handling function for HF Client
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_sm_execute(UINT16 event, tBTA_HF_CLIENT_DATA *p_data)
{
    tBTA_HF_CLIENT_ST_TBL      state_table;
    UINT8               action;
    int                 i;

#if BTA_HF_CLIENT_DEBUG == TRUE
    UINT16  in_event = event;
    UINT8 in_state =  bta_hf_client_cb.scb.state;

    /* Ignore displaying of AT results when not connected (Ignored in state machine) */
    if (bta_hf_client_cb.scb.state == BTA_HF_CLIENT_OPEN_ST) {
        APPL_TRACE_EVENT("HF Client evt : State %d (%s), Event 0x%04x (%s)",
                         bta_hf_client_cb.scb.state,
                         bta_hf_client_state_str(bta_hf_client_cb.scb.state),
                         event, bta_hf_client_evt_str(event));
    }
#endif

    event &= 0x00FF;
    if (event >= (BTA_HF_CLIENT_MAX_EVT & 0x00FF)) {
        APPL_TRACE_ERROR("HF Client evt out of range, ignoring...");
        return;
    }

    /* look up the state table for the current state */
    state_table = bta_hf_client_st_tbl[bta_hf_client_cb.scb.state];

    /* set next state */
    bta_hf_client_cb.scb.state = state_table[event][BTA_HF_CLIENT_NEXT_STATE];

    /* execute action functions */
    for (i = 0; i < BTA_HF_CLIENT_ACTIONS; i++) {
        if ((action = state_table[event][i]) != BTA_HF_CLIENT_IGNORE) {
            (*bta_hf_client_action[action])(p_data);
        } else {
            break;
        }
    }

#if BTA_HF_CLIENT_DEBUG == TRUE
    if (bta_hf_client_cb.scb.state != in_state) {
        APPL_TRACE_EVENT("BTA HF Client State Change: [%s] -> [%s] after Event [%s]",
                         bta_hf_client_state_str(in_state),
                         bta_hf_client_state_str(bta_hf_client_cb.scb.state),
                         bta_hf_client_evt_str(in_event));
    }
#endif
}

static void send_post_slc_cmd(void)
{
    bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE;

    bta_hf_client_send_at_bia();
    bta_hf_client_send_at_ccwa(TRUE);
    bta_hf_client_send_at_cmee(TRUE);
    bta_hf_client_send_at_cops(FALSE);
    bta_hf_client_send_at_btrh(TRUE, 0);
    bta_hf_client_send_at_clip(TRUE);
}

/*******************************************************************************
**
** Function         bta_hf_client_slc_seq
**
** Description      Handles AT commands sequence required for SLC creation
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_slc_seq(BOOLEAN error)
{
    APPL_TRACE_DEBUG("bta_hf_client_slc_seq cmd: %u", bta_hf_client_cb.scb.at_cb.current_cmd);

    if (error) {
        /* SLC establishment error, sent close rfcomm event */
        APPL_TRACE_ERROR("HFPClient: Failed to create SLC due to AT error, disconnecting (%u)",
                         bta_hf_client_cb.scb.at_cb.current_cmd);

        bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL);
        return;
    }

    if (bta_hf_client_cb.scb.svc_conn) {
        return;
    }

    switch (bta_hf_client_cb.scb.at_cb.current_cmd) {
    case BTA_HF_CLIENT_AT_NONE:
        bta_hf_client_send_at_brsf();
        break;

    case BTA_HF_CLIENT_AT_BRSF:
        if (bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_CODEC) {
            bta_hf_client_send_at_bac();
            break;
        }

        bta_hf_client_send_at_cind(FALSE);
        break;

    case BTA_HF_CLIENT_AT_BAC:
        bta_hf_client_send_at_cind(FALSE);
        break;

    case BTA_HF_CLIENT_AT_CIND:
        bta_hf_client_send_at_cind(TRUE);
        break;

    case BTA_HF_CLIENT_AT_CIND_STATUS:
        bta_hf_client_send_at_cmer(TRUE);
        break;

    case BTA_HF_CLIENT_AT_CMER:
        if (bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_FEAT_3WAY) {
            bta_hf_client_send_at_chld('?', 0);
        } else {
            bta_hf_client_svc_conn_open(NULL);
            send_post_slc_cmd();
        }
        break;

    case BTA_HF_CLIENT_AT_CHLD:
        bta_hf_client_svc_conn_open(NULL);
        send_post_slc_cmd();
        break;

    default:
        /* If happen there is a bug in SLC creation procedure... */
        APPL_TRACE_ERROR("HFPClient: Failed to create SLCdue to unexpected AT command, disconnecting (%u)",
                         bta_hf_client_cb.scb.at_cb.current_cmd);

        bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL);
        break;
    }
}

#if BTA_HF_CLIENT_DEBUG == TRUE

#ifndef CASE_RETURN_STR
#define CASE_RETURN_STR(const) case const: return #const;
#endif

static char *bta_hf_client_evt_str(UINT16 event)
{
    switch (event) {
        CASE_RETURN_STR(BTA_HF_CLIENT_API_REGISTER_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_DEREGISTER_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_OPEN_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_CLOSE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_AUDIO_OPEN_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_RFC_OPEN_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_RFC_CLOSE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_RFC_DATA_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_DISC_ACP_RES_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_DISC_INT_RES_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_DISC_OK_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_DISC_FAIL_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_ENABLE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_API_DISABLE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_SCO_OPEN_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_SCO_CLOSE_EVT)
        CASE_RETURN_STR(BTA_HF_CLIENT_SEND_AT_CMD_EVT)
    default:
        return "Unknown HF Client Event";
    }
}

static char *bta_hf_client_state_str(UINT8 state)
{
    switch (state) {
        CASE_RETURN_STR(BTA_HF_CLIENT_INIT_ST)
        CASE_RETURN_STR(BTA_HF_CLIENT_OPENING_ST)
        CASE_RETURN_STR(BTA_HF_CLIENT_OPEN_ST)
        CASE_RETURN_STR(BTA_HF_CLIENT_CLOSING_ST)
    default:
        return "Unknown HF Client State";
    }
}
#endif
#endif /* #if (BTA_HF_INCLUDED == TRUE) */
