Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d83a716 authored by Matthew Xie's avatar Matthew Xie Committed by Android Git Automerger
Browse files

am 4b59ce28: Add WBS support on Bluedroid (2/6)

* commit '4b59ce28':
  Add WBS support on Bluedroid (2/6)
parents 931c0cfd 4b59ce28
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
@@ -423,6 +423,10 @@ void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
#if (BTM_WBS_INCLUDED == TRUE )
    p_scb->peer_codecs = BTA_AG_CODEC_NONE;
    p_scb->sco_codec = BTA_AG_CODEC_NONE;
    /* Clear these flags upon SLC teardown */
    p_scb->codec_updated = FALSE;
    p_scb->codec_fallback = FALSE;
    p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
    p_scb->role = 0;
    p_scb->post_sco = BTA_AG_POST_SCO_NONE;
@@ -876,3 +880,51 @@ void bta_ag_rcvd_slc_ready(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
    }
}

/*******************************************************************************
**
** Function         bta_ag_setcodec
**
** Description      Handle API SetCodec
**
**
** Returns          void
**
*******************************************************************************/
void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
#if (BTM_WBS_INCLUDED == TRUE )
    tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec;
    tBTA_AG_VAL        val;

    /* Check if the requested codec type is valid */
    if((codec_type != BTA_AG_CODEC_NONE) &&
       (codec_type != BTA_AG_CODEC_CVSD) &&
       (codec_type != BTA_AG_CODEC_MSBC))
    {
        val.num = codec_type;
        val.hdr.status = BTA_AG_FAIL_RESOURCES;
        APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
        (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val);
        return;
    }

    if((p_scb->peer_codecs & codec_type) || (codec_type == BTA_AG_CODEC_NONE) ||
        (codec_type == BTA_AG_CODEC_CVSD))
    {
        p_scb->sco_codec = codec_type;
        p_scb->codec_updated = TRUE;
        val.num = codec_type;
        val.hdr.status = BTA_AG_SUCCESS;
        APPL_TRACE_DEBUG("bta_ag_setcodec: Updated codec type %d", codec_type);
    }
    else
    {
        val.num = codec_type;
        val.hdr.status = BTA_AG_FAIL_RESOURCES;
        APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
    }

    (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG *) &val);
#endif
}

system/bta/ag/bta_ag_cmd.c

100644 → 100755
+9 −43
Original line number Diff line number Diff line
@@ -1203,7 +1203,10 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
                    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))
                {
@@ -1218,6 +1221,8 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
            break;

        case BTA_AG_HF_CMD_BCS:
            bta_ag_send_ok(p_scb);

            /* stop cn timer */
            bta_sys_stop_timer(&p_scb->cn_timer);

@@ -1241,7 +1246,8 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
            else
                bta_ag_sco_codec_nego(p_scb, FALSE);

            bta_ag_send_ok(p_scb);
            /* send final codec info to callback */
            val.num = codec_sent;
            break;

        case BTA_AG_HF_CMD_BCC:
@@ -1718,47 +1724,6 @@ void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
    }
}

/*******************************************************************************
**
** Function         bta_ag_setcodec
**
** Description      Handle API SetCodec
**
**
** Returns          void
**
*******************************************************************************/
void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
{
#if (BTM_WBS_INCLUDED == TRUE )
    tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec;

    /* Check if the requested codec type is valid */
    if((codec_type != BTA_AG_CODEC_NONE) &&
       (codec_type != BTA_AG_CODEC_CVSD) &&
       (codec_type != BTA_AG_CODEC_MSBC))
    {
        APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
        return;
    }

    if((p_scb->peer_codecs & codec_type) || (codec_type == BTA_AG_CODEC_NONE) || (codec_type == BTA_AG_CODEC_CVSD))
    {
        p_scb->sco_codec = codec_type;
        p_scb->codec_updated = TRUE;
        APPL_TRACE_DEBUG("bta_ag_setcodec: Updated codec type %d", codec_type);
    }
    else
    {
        APPL_TRACE_ERROR("bta_ag_setcodec error: unsupported codec type %d", codec_type);
    }
#else
    UNUSED(p_scb);
    UNUSED(p_data);
#endif
}


#if (BTM_WBS_INCLUDED == TRUE )
/*******************************************************************************
**
@@ -1792,6 +1757,7 @@ void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
    }

    /* send +BCS */
    APPL_TRACE_DEBUG("send +BCS codec is %d", codec_uuid);
    bta_ag_send_result(p_scb, BTA_AG_RES_BCS, NULL, codec_uuid);

}
+9 −0
Original line number Diff line number Diff line
@@ -237,6 +237,14 @@ typedef struct
    UINT8           scn;
} tBTA_AG_PROFILE;

#if (BTM_WBS_INCLUDED == TRUE)
typedef enum
{
    BTA_AG_SCO_MSBC_SETTINGS_T2 = 0, /* preferred/default when codec is mSBC */
    BTA_AG_SCO_MSBC_SETTINGS_T1,
} tBTA_AG_SCO_MSBC_SETTINGS;
#endif

/* type for each service control block */
typedef struct
{
@@ -261,6 +269,7 @@ typedef struct
    tBTA_AG_PEER_CODEC  inuse_codec;    /* codec being used for the current SCO connection */
    BOOLEAN             codec_updated;  /* set to TRUE whenever the app updates codec type */
    BOOLEAN             codec_fallback; /* If sco nego fails for mSBC, fallback to CVSD */
    tBTA_AG_SCO_MSBC_SETTINGS codec_msbc_settings; /* settings to be used for the impending eSCO */
    TIMER_LIST_ENT      cn_timer;       /* codec negotiation timer */
#endif
    UINT16              sco_idx;        /* SCO handle */
+7 −2
Original line number Diff line number Diff line
@@ -318,11 +318,16 @@ static tBTA_AG_SCB *bta_ag_scb_alloc(void)
            /* initialize variables */
            p_scb->in_use = TRUE;
            p_scb->sco_idx = BTM_INVALID_SCO_INDEX;

#if (BTM_WBS_INCLUDED == TRUE )
            p_scb->codec_updated = FALSE;
#endif
            /* set up timers */
            p_scb->act_timer.param = (UINT32) p_scb;
            p_scb->act_timer.p_cback = bta_ag_timer_cback;

#if (BTM_WBS_INCLUDED == TRUE)
            /* set eSCO mSBC setting to T2 as the preferred */
            p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
            APPL_TRACE_DEBUG("bta_ag_scb_alloc %d", bta_ag_scb_to_idx(p_scb));
            break;
        }
+115 −40
Original line number Diff line number Diff line
@@ -69,7 +69,11 @@ enum
};

#if (BTM_WBS_INCLUDED == TRUE )
#define BTA_AG_NUM_CODECS   2
#define BTA_AG_NUM_CODECS   3
#define BTA_AG_ESCO_SETTING_IDX_CVSD    0   /* eSCO setting for CVSD */
#define BTA_AG_ESCO_SETTING_IDX_T1      1   /* eSCO setting for mSBC T1 */
#define BTA_AG_ESCO_SETTING_IDX_T2      2   /* eSCO setting for mSBC T2 */

static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] =
{
    /* CVSD */
@@ -88,7 +92,20 @@ static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] =
        BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
        BTM_ESCO_RETRANS_POWER       /* Retransmission effort                      */
    },
    /* mSBC */
    /* mSBC  T1 */
    {
        BTM_64KBITS_RATE,                   /* TX Bandwidth (64 kbits/sec), 8000        */
        BTM_64KBITS_RATE,                   /* RX Bandwidth (64 kbits/sec), 8000        */
        8,                                  /* 8 ms                                     */
        BTM_VOICE_SETTING_TRANS,            /* Inp Linear, Transparent, 2s Comp, 16bit  */
       (BTM_SCO_PKT_TYPES_MASK_EV3      |   /* Packet Types : EV3 + NO_2_EV3            */
        BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 |
        BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
        BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 |
        BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 ),
        BTM_ESCO_RETRANS_QUALITY       /* Retransmission effort                      */
    },
    /* mSBC T2*/
    {
        BTM_64KBITS_RATE,                   /* TX Bandwidth (64 kbits/sec), 8000        */
        BTM_64KBITS_RATE,                   /* RX Bandwidth (64 kbits/sec), 8000        */
@@ -102,6 +119,7 @@ static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] =
    }
};
#else
/* WBS not included, CVSD by default */
static const tBTM_ESCO_PARAMS bta_ag_esco_params =
{
    BTM_64KBITS_RATE,                   /* TX Bandwidth (64 kbits/sec)              */
@@ -216,14 +234,23 @@ static void bta_ag_sco_disc_cback(UINT16 sco_idx)
        /* Restore settings */
        if(bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC)
        {
            BTM_SetWBSCodec (BTM_SCO_CODEC_NONE);
            /* set_sco_codec(BTM_SCO_CODEC_NONE); we should get a close */
            BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);

            /* If SCO open was initiated by AG and failed for mSBC, try CVSD again. */
            /* If SCO open was initiated by AG and failed for mSBC, then attempt
            mSBC with T1 settings i.e. 'Safe Settings'. If this fails, then switch to CVSD */
            if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb))
            {
                if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2)
                {
                     APPL_TRACE_DEBUG("Fallback to mSBC T1 settings");
                     bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T1;
                }
                else
                {
                    APPL_TRACE_DEBUG("Fallback to CVSD settings");
                    bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE;
                APPL_TRACE_DEBUG("Fallback to CVSD");
                }
            }
        }

@@ -450,11 +477,23 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
        p_scb->codec_updated = TRUE;
    }

    /* If WBS included, use CVSD by default, index is 0 for CVSD by initialization */
    /* If eSCO codec is mSBC, index is T2 or T1 */
    if (esco_codec == BTM_SCO_CODEC_MSBC)
        codec_index = esco_codec - 1;
    {
        if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2)
        {
            codec_index = BTA_AG_ESCO_SETTING_IDX_T2;
        }
        else
        {
            codec_index = BTA_AG_ESCO_SETTING_IDX_T1;
        }
    }

    params = bta_ag_esco_params[codec_index];
#else
    /* When WBS is not included, use CVSD by default */
    params = bta_ag_esco_params;
#endif

@@ -464,7 +503,7 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
    if(!bta_ag_cb.sco.param_updated)
    {
#if (BTM_WBS_INCLUDED == TRUE)
        if (!codec_index)   /* For non-WBS */
        if (esco_codec == BTM_SCO_CODEC_CVSD)   /* For CVSD */
#endif
        {
            /* Use the application packet types (5 slot EV packets not allowed) */
@@ -518,8 +557,23 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
        /* tell sys to stop av if any */
        bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);

#if (BTM_WBS_INCLUDED == TRUE )
        /* Allow any platform specific pre-SCO set up to take place */
        bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP,\
        esco_codec);

        /* This setting may not be necessary */
        /* To be verified with stable 2049 boards */
        if (esco_codec == BTA_AG_CODEC_MSBC)
            BTM_WriteVoiceSettings (BTM_VOICE_SETTING_TRANS);
        else
            BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);
        /* save the current codec because sco_codec can be updated while SCO is open. */
        p_scb->inuse_codec = esco_codec;
#else
        /* Allow any platform specific pre-SCO set up to take place */
        bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP);
#endif

#if (BTM_SCO_HCI_INCLUDED == TRUE )
#if (BTM_WBS_INCLUDED == TRUE)
@@ -532,26 +586,6 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
        sco_route = bta_dm_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id);
#endif

#if (BTM_WBS_INCLUDED == TRUE )
        if (esco_codec == BTA_AG_CODEC_MSBC)
        {
            /* Enable mSBC codec in fw */
            BTM_SetWBSCodec (esco_codec);
        }

        /* Specify PCM input for SBC codec in fw */
        BTM_ConfigI2SPCM (esco_codec, (UINT8)HCI_BRCM_I2SPCM_IS_DEFAULT_ROLE, (UINT8)HCI_BRCM_I2SPCM_SAMPLE_DEFAULT, (UINT8)HCI_BRCM_I2SPCM_CLOCK_DEFAULT);

        /* This setting may not be necessary */
        /* To be verified with stable 2049 boards */
        if (esco_codec == BTA_AG_CODEC_MSBC)
            BTM_WriteVoiceSettings (BTM_VOICE_SETTING_TRANS);
        else
            BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);

        /* save the current codec because sco_codec can be updated while SCO is open. */
        p_scb->inuse_codec = esco_codec;
#endif

#if (BTM_SCO_HCI_INCLUDED == TRUE )
        /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */
@@ -584,6 +618,25 @@ static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
}

#if (BTM_WBS_INCLUDED == TRUE )
/*******************************************************************************
**
** Function         bta_ag_attempt_msbc_safe_settings
**
** Description    Checks if ESCO connection needs to be attempted using mSBC T1(safe) settings
**
**
** Returns          TRUE if T1 settings has to be used, FALSE otherwise
**
*******************************************************************************/
BOOLEAN bta_ag_attempt_msbc_safe_settings(tBTA_AG_SCB *p_scb)
{
    if (p_scb->svc_conn && p_scb->sco_codec == BTM_SCO_CODEC_MSBC &&
        p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1)
        return TRUE;
    else
        return FALSE;
}

/*******************************************************************************
**
** Function         bta_ag_cn_timer_cback
@@ -627,7 +680,9 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb)
{
    bta_ag_cb.sco.p_curr_scb = p_scb;

    if (p_scb->codec_updated || p_scb->codec_fallback)
    if ((p_scb->codec_updated || p_scb->codec_fallback ||
        bta_ag_attempt_msbc_safe_settings(p_scb)) &&
       (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC))
    {
        /* Change the power mode to Active until sco open is completed. */
        bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
@@ -643,10 +698,11 @@ void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb)
    else
    {
        /* use same codec type as previous SCO connection, skip codec negotiation */
        APPL_TRACE_DEBUG("use same codec type as previous SCO connection,skip codec negotiation");
        bta_ag_sco_codec_nego(p_scb, TRUE);
    }
}
#endif
#endif /* (BTM_WBS_INCLUDED == TRUE ) */

/*******************************************************************************
**
@@ -1399,7 +1455,12 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)

    bta_sys_sco_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);

#if (BTM_WBS_INCLUDED == TRUE)
    bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_ON,
                          p_scb->inuse_codec);
#else
    bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_ON);
#endif

#if (BTM_SCO_HCI_INCLUDED == TRUE )
    /* open SCO codec if SCO is routed through transport */
@@ -1410,6 +1471,10 @@ void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
    bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT);

    p_scb->retry_with_sco_only = FALSE;
#if (BTM_WBS_INCLUDED == TRUE)
    /* reset to mSBC T2 settings as the preferred */
    p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
}

/*******************************************************************************
@@ -1433,7 +1498,9 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)

#if (BTM_WBS_INCLUDED == TRUE)
    /* codec_fallback is set when AG is initiator and connection failed for mSBC. */
    if (p_scb->codec_fallback && p_scb->svc_conn)
    /* OR if codec is msbc and T2 settings failed, then retry Safe T1 settings */
    if ((p_scb->codec_fallback && p_scb->svc_conn) ||
         bta_ag_attempt_msbc_safe_settings(p_scb))
    {
        bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E);
    }
@@ -1452,12 +1519,15 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
#endif
    else
    {
#if (BTM_WBS_INCLUDED == TRUE)
        /* Indicate if the closing of audio is because of transfer */
        if (bta_ag_cb.sco.p_xfer_scb)
            bta_ag_co_audio_state(handle, p_scb->app_id, BTA_AG_CO_AUD_STATE_OFF_XFER);
        else
            bta_ag_co_audio_state(handle, p_scb->app_id, BTA_AG_CO_AUD_STATE_OFF);

        bta_ag_co_audio_state(handle, p_scb->app_id,(bta_ag_cb.sco.p_xfer_scb)?\
        BTA_AG_CO_AUD_STATE_OFF_XFER:BTA_AG_CO_AUD_STATE_OFF,p_scb->inuse_codec);
#else
        /* Indicate if the closing of audio is because of transfer */
        bta_ag_co_audio_state(handle, p_scb->app_id,(bta_ag_cb.sco.p_xfer_scb)?\
        BTA_AG_CO_AUD_STATE_OFF_XFER:BTA_AG_CO_AUD_STATE_OFF);
#endif
        bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E);

        bta_sys_sco_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
@@ -1472,6 +1542,9 @@ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)

        /* call app callback */
        bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT);
#if (BTM_WBS_INCLUDED == TRUE)
        p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2;
#endif
    }
    p_scb->retry_with_sco_only = FALSE;
}
@@ -1531,12 +1604,14 @@ void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data
        /* tell sys to stop av if any */
        bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);

#if (BTM_WBS_INCLUDED == FALSE )
        /* Allow any platform specific pre-SCO set up to take place */
        bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP);

#if (BTM_WBS_INCLUDED == TRUE )
#else
        /* When HS initiated SCO, it cannot be WBS. */
        BTM_ConfigI2SPCM (BTM_SCO_CODEC_CVSD, (UINT8)HCI_BRCM_I2SPCM_IS_DEFAULT_ROLE, (UINT8)HCI_BRCM_I2SPCM_SAMPLE_DEFAULT, (UINT8)HCI_BRCM_I2SPCM_CLOCK_DEFAULT);
        /* Allow any platform specific pre-SCO set up to take place */
        bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP,
                              BTA_AG_CODEC_CVSD);
#endif

#if (BTM_SCO_HCI_INCLUDED == TRUE )
Loading