/*******************************************************************************
**
** Function         bta_dm_pm_set_sniff_policy
**
** Description      Disables/Enables sniff in link policy for the give device
**
** Returns          None
**
*******************************************************************************/
static void bta_dm_pm_set_sniff_policy(tBTA_DM_PEER_DEVICE *p_dev, BOOLEAN bDisable)
{
    UINT16 policy_setting;

    if (!p_dev)
        return;

    if (bDisable)
    {
        policy_setting = bta_dm_cb.cur_policy &
            (HCI_ENABLE_MASTER_SLAVE_SWITCH |
             HCI_ENABLE_HOLD_MODE  |
             HCI_ENABLE_PARK_MODE);

    }
    else
    {
        /*  allow sniff after sco is closed */
         policy_setting= bta_dm_cb.cur_policy;
    }

    /* if disabling SNIFF, make sure link is Active */
    if (bDisable)
        bta_dm_pm_active(p_dev->peer_bdaddr);

    /* update device record and set link policy */
    p_dev->link_policy = policy_setting;
    BTM_SetLinkPolicy(p_dev->peer_bdaddr, &policy_setting);

}
/*******************************************************************************
**
** Function         bta_hf_client_start_close
**
** Description      Start the process of closing SCO and RFCOMM connection.
**
**
** Returns          void
**
*******************************************************************************/
void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA *p_data)
{
    /* Take the link out of sniff and set L2C idle time to 0 */
    bta_dm_pm_active(bta_hf_client_cb.scb.peer_addr);
    L2CA_SetIdleTimeoutByBdAddr(bta_hf_client_cb.scb.peer_addr, 0, BT_TRANSPORT_BR_EDR);

    /* if SCO is open close SCO and wait on RFCOMM close */
    if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_OPEN_ST)
    {
        bta_hf_client_cb.scb.sco_close_rfc = TRUE;
    }
    else
    {
        bta_hf_client_rfc_do_close(p_data);
    }

    /* always do SCO shutdown to handle all SCO corner cases */
    bta_hf_client_sco_shutdown(NULL);
}
/*******************************************************************************
**
** Function         bta_ag_start_close
**
** Description      Start the process of closing SCO and RFCOMM connection.
**
**
** Returns          void
**
*******************************************************************************/
void bta_ag_start_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
    /* Take the link out of sniff and set L2C idle time to 0 */
    bta_dm_pm_active(p_scb->peer_addr);
    L2CA_SetIdleTimeoutByBdAddr(p_scb->peer_addr, 0);

    /* if SCO is open close SCO and wait on RFCOMM close */
    if (bta_ag_sco_is_open(p_scb))
    {
        p_scb->post_sco = BTA_AG_POST_SCO_CLOSE_RFC;
    }
    else
    {
        p_scb->post_sco = BTA_AG_POST_SCO_NONE;
        bta_ag_rfc_do_close(p_scb, p_data);
    }

    /* always do SCO shutdown to handle all SCO corner cases */
    bta_ag_sco_shutdown(p_scb, p_data);
}
/*******************************************************************************
**
** Function         bta_dm_pm_set_mode
**
** Description      Set the power mode for the device
**
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_pm_set_mode(BD_ADDR peer_addr, BOOLEAN timed_out )
{

    tBTA_DM_PM_ACTTION  pm_action = BTA_DM_PM_NO_ACTION;
    UINT16              timeout = 0;
    UINT8               i,j;
    tBTA_DM_PM_ACTTION  failed_pm = 0;
    tBTA_DM_PEER_DEVICE *p_peer_device = NULL;
    tBTA_DM_PM_ACTTION   allowed_modes = 0;
    tBTA_DM_PM_ACTTION   pref_modes = 0;
    tBTA_DM_PM_CFG      *p_pm_cfg;
    tBTA_DM_PM_SPEC     *p_pm_spec;
    tBTA_DM_PM_ACTN     *p_act0, *p_act1;
    tBTA_DM_SRVCS       *p_srvcs;


    if(!bta_dm_cb.device_list.count)
        return;

    /* see if any attempt to put device in low power mode failed */
    p_peer_device = bta_dm_find_peer_device(peer_addr);
    /* if no peer device found return */
    if (p_peer_device == NULL)
        return;

    failed_pm = p_peer_device->pm_mode_failed;

    for(i=0; i<bta_dm_conn_srvcs.count ; i++)
    {

        p_srvcs = &bta_dm_conn_srvcs.conn_srvc[i];
        if(!bdcmp(p_srvcs->peer_bdaddr, peer_addr))
        {

            /* p_bta_dm_pm_cfg[0].app_id is the number of entries */
            for(j=1; j<=p_bta_dm_pm_cfg[0].app_id; j++)
            {
                if((p_bta_dm_pm_cfg[j].id == p_srvcs->id)
                    && ((p_bta_dm_pm_cfg[j].app_id == BTA_ALL_APP_ID ) ||
                    (p_bta_dm_pm_cfg[j].app_id == p_srvcs->app_id)))
                    break;
            }

            p_pm_cfg = &p_bta_dm_pm_cfg[j];
            p_pm_spec = &p_bta_dm_pm_spec[p_pm_cfg->spec_idx];
            p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state][0];
            p_act1 = &p_pm_spec->actn_tbl[p_srvcs->state][1];

            APPL_TRACE_DEBUG("bta_dm_pm_set_mode: srvcsid: %d, state: %d, j: %d", p_srvcs->id, p_srvcs->state, j);
            allowed_modes |= p_pm_spec->allow_mask;

            /* PM actions are in the order of strictness */

            /* first check if the first preference is ok */
            if(!(failed_pm & p_act0->power_mode))
            {
                pref_modes |= p_act0->power_mode;

                if(p_act0->power_mode > pm_action)
                {
                    pm_action = p_act0->power_mode;
                    timeout =  p_act0->timeout;

                }
            }
            /* if first preference has already failed, try second preference */
            else if(!(failed_pm & p_act1->power_mode))
            {
                pref_modes |= p_act1->power_mode;

                if(p_act1->power_mode > pm_action)
                {
                    pm_action = p_act1->power_mode;
                    timeout =  p_act1->timeout;

                }
            }
        }
    }

    if(pm_action & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF))
    {

        /* some service don't like the mode */
        if(!(allowed_modes & pm_action))
        {

            /* select the other mode if its allowed and preferred, otherwise 0 which is BTA_DM_PM_NO_ACTION */
            pm_action =  (allowed_modes & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF) & pref_modes);

            /* no timeout needed if no action is required */
            if(pm_action == BTA_DM_PM_NO_ACTION)
            {
                timeout = 0;
            }

        }


    }

    if(!timed_out && timeout)
    {

        for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
        {

            if(!bta_dm_cb.pm_timer[i].in_use)
            {
                bta_dm_cb.pm_timer[i].in_use = TRUE;
                bdcpy(bta_dm_cb.pm_timer[i].peer_bdaddr, peer_addr);
                bta_dm_cb.pm_timer[i].timer.p_cback = bta_dm_pm_timer_cback;
                bta_sys_start_timer(&bta_dm_cb.pm_timer[i].timer, 0, timeout);
                APPL_TRACE_DEBUG("start dm_pm_timer:%d, %d", i, timeout);
                return;

            }

        }

        /* no more timers */
        if(i==BTA_DM_NUM_PM_TIMER)
        {
            APPL_TRACE_WARNING("bta_dm_act dm_pm_timer no more");
            return;
        }
    }

    if(pm_action == BTA_DM_PM_NO_ACTION)
    {


    }
    else if(pm_action == BTA_DM_PM_PARK)
    {
        p_peer_device->pm_mode_attempted = BTA_DM_PM_PARK;
        bta_dm_pm_park(peer_addr);

    }
    else if(pm_action & BTA_DM_PM_SNIFF)
    {
        /* dont initiate SNIFF, if link_policy has it disabled */
        if (p_peer_device->link_policy & HCI_ENABLE_SNIFF_MODE)
        {
            p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF;
            bta_dm_pm_sniff(p_peer_device, (UINT8)(pm_action & 0x0F) );
        }
        else
        {
            APPL_TRACE_DEBUG("bta_dm_pm_set_mode: Link policy disallows SNIFF, ignore request");
        }
    }
    else if(pm_action == BTA_DM_PM_ACTIVE)
    {

        bta_dm_pm_active(peer_addr);

    }


}