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

// TODO(armansito): cutils/properties.h is only being used to pull-in runtime
// settings on Android. Remove this conditional include once we have a generic
// way to obtain system properties.
#if !defined(OS_GENERIC)
#include <cutils/properties.h>
#endif  // !defined(OS_GENERIC)

#include "osi/include/osi.h"
#include "bt_utils.h"
#include "bta_api.h"
#include "bta_sys.h"
#include "bta_hf_client_api.h"
#include "bta_hf_client_int.h"

/* uncomment to enable extra debug */
/* #define BTA_HF_CLIENT_DEBUG TRUE */

#ifndef BTA_HF_CLIENT_DEBUG
#define BTA_HF_CLIENT_DEBUG FALSE
#endif

extern fixed_queue_t *btu_bta_alarm_queue;

#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,
    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,
};

/* 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},
};

/* 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},
};

/* 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},
};

/* 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},
};

/* 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 */
tBTA_HF_CLIENT_CB  bta_hf_client_cb;

/*******************************************************************************
**
** 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__);

    alarm_free(bta_hf_client_cb.scb.collision_timer);
    alarm_free(bta_hf_client_cb.scb.at_cb.resp_timer);
    alarm_free(bta_hf_client_cb.scb.at_cb.hold_timer);
    memset(&bta_hf_client_cb.scb, 0, sizeof(tBTA_HF_CLIENT_SCB));
    bta_hf_client_cb.scb.collision_timer =
      alarm_new("bta_hf_client.scb_collision_timer");
    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_collision_timer_cback
**
** Description      HF Client connection collision timer callback
**
**
** Returns          void
**
*******************************************************************************/
static void bta_hf_client_collision_timer_cback(UNUSED_ATTR void *data)
{
    APPL_TRACE_DEBUG("%s", __FUNCTION__);

    /* 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 */
        alarm_set_on_queue(bta_hf_client_cb.scb.collision_timer,
                           BTA_HF_CLIENT_COLLISION_TIMER_MS,
                           bta_hf_client_collision_timer_cback,
                           NULL,
                           btu_bta_alarm_queue);
    }
}

/*******************************************************************************
**
** 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)
{
    // TODO(armansito): For non-Android systems we need a common method of
    // loading system properties. Remove the conditionally compiled code once we
    // have that in place.
#if !defined(OS_GENERIC)
    char value[PROPERTY_VALUE_MAX];
#endif  // !defined(OS_GENERIC)

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

    // TODO(armansito): For non-Android systems we need a common method of
    // loading system properties. Remove the conditionally compiled code once we
    // have that in place.
#if !defined(OS_GENERIC)
    /* check if mSBC support enabled */
    property_get("ro.bluetooth.hfp.ver", value, "0");
    if (strcmp(value,"1.6") == 0)
    {
       bta_hf_client_cb.msbc_enabled = TRUE;
    }
#endif  // !defined(OS_GENERIC)

    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.features & BTA_HF_CLIENT_FEAT_CODEC)
                && (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_cb.scb.features & BTA_HF_CLIENT_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
