status_t SpeechEnhancementController::SetMagiConSpeechParametersToAllModem(const AUDIO_CUSTOM_MAGI_CONFERENCE_STRUCT *pSphParamMagiCon)
{
    SpeechDriverFactory *pSpeechDriverFactory = SpeechDriverFactory::GetInstance();
    SpeechDriverInterface *pSpeechDriver = NULL;

    for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++)
    {
        pSpeechDriver = pSpeechDriverFactory->GetSpeechDriverByIndex((modem_index_t)modem_index);
        if (pSpeechDriver != NULL) // Might be single talk and some speech driver is NULL
        {
            pSpeechDriver->SetMagiConSpeechParameters(pSphParamMagiCon);
        }
    }

    return NO_ERROR;
}
void SpeechEnhancementController::SetBtHeadsetNrecOnToAllModem(const bool bt_headset_nrec_on)
{
    SpeechDriverFactory *pSpeechDriverFactory = SpeechDriverFactory::GetInstance();
    SpeechDriverInterface *pSpeechDriver = NULL;

    property_set(PROPERTY_KEY_BT_HEADSET_NREC_ON, (bt_headset_nrec_on == false) ? "0" : "1");
    mBtHeadsetNrecOn = bt_headset_nrec_on;

    for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++)
    {
        pSpeechDriver = pSpeechDriverFactory->GetSpeechDriverByIndex((modem_index_t)modem_index);
        if (pSpeechDriver != NULL) // Might be single talk and some speech driver is NULL
        {
            pSpeechDriver->SetBtHeadsetNrecOn(mBtHeadsetNrecOn);
        }
    }
}
status_t SpeechEnhancementController::SetSpeechEnhancementMaskToAllModem(const sph_enh_mask_struct_t &mask)
{
    char property_value[PROPERTY_VALUE_MAX];
    sprintf(property_value, "0x%x 0x%x", mask.main_func, mask.dynamic_func);
    property_set(PROPERTY_KEY_SPH_ENH_MASKS, property_value);

    mSpeechEnhancementMask = mask;

    SpeechDriverFactory *pSpeechDriverFactory = SpeechDriverFactory::GetInstance();
    SpeechDriverInterface *pSpeechDriver = NULL;

    for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++) {
        pSpeechDriver = pSpeechDriverFactory->GetSpeechDriverByIndex((modem_index_t)modem_index);
        if (pSpeechDriver != NULL) { // Might be single talk and some speech driver is NULL
            pSpeechDriver->SetSpeechEnhancementMask(mSpeechEnhancementMask);
        }
    }

    return NO_ERROR;
}
status_t AudioALSACaptureDataProviderVoice::open()
{
    ALOGD("%s()", __FUNCTION__);
    ASSERT(mClientLock.tryLock() != 0); // lock by base class attach
    AudioAutoTimeoutLock _l(mEnableLock);

    ASSERT(mEnable == false);

    // config attribute (will used in client SRC/Enh/... later)
    SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriver();

    mStreamAttributeSource.audio_format = AUDIO_FORMAT_PCM_16_BIT;
    mStreamAttributeSource.num_channels = pSpeechDriver->GetRecordChannelNumber();
    mStreamAttributeSource.audio_channel_mask = (mStreamAttributeSource.num_channels == 1) ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO;
    mStreamAttributeSource.sample_rate = pSpeechDriver->GetRecordSampleRate();

    mEnable = true;

    OpenPCMDump(LOG_TAG);

    return SpeechDriverFactory::GetInstance()->GetSpeechDriver()->RecordOn();
}
status_t SpeechEnhancementController::SetNBSpeechParametersToAllModem(const AUDIO_CUSTOM_PARAM_STRUCT *pSphParamNB)
{
    SpeechDriverFactory *pSpeechDriverFactory = SpeechDriverFactory::GetInstance();
    SpeechDriverInterface *pSpeechDriver = NULL;
    AUDIO_CUSTOM_PARAM_STRUCT mSphParamNB;

    if (mSMNROn == true)
    {
        //forcely set single mic setting
        ALOGD("%s(), mSMNROn = %d, set single mic setting", __FUNCTION__, mSMNROn);
        memcpy(&mSphParamNB, pSphParamNB, sizeof(AUDIO_CUSTOM_PARAM_STRUCT));
        for (int speech_mode_index = 0; speech_mode_index < 8; speech_mode_index++)
        {
            (mSphParamNB.speech_mode_para[speech_mode_index][13]) = 0;
            (mSphParamNB.speech_mode_para[speech_mode_index][14]) = 0;

        }
    }


    for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++)
    {
        pSpeechDriver = pSpeechDriverFactory->GetSpeechDriverByIndex((modem_index_t)modem_index);
        if (pSpeechDriver != NULL) // Might be single talk and some speech driver is NULL
        {
            if (mSMNROn != true)
            {
                pSpeechDriver->SetNBSpeechParameters(pSphParamNB);
            }
            else
            {
                pSpeechDriver->SetNBSpeechParameters(&mSphParamNB);
            }
        }
    }

    return NO_ERROR;
}
status_t LoopbackManager::SetLoopbackOn(loopback_t loopback_type, loopback_output_device_t loopback_output_device)
{
    Mutex::Autolock _l(mLock);

    ALOGD("+%s(), loopback_type = %d, loopback_output_device = %d", __FUNCTION__,  loopback_type, loopback_output_device);

    if (mLoopbackType != NO_LOOPBACK) { // check no loobpack function on
        ALOGD("-%s() : Please Turn off Loopback Type %d First!!", __FUNCTION__, mLoopbackType);
        return ALREADY_EXISTS;
    }
    else if (CheckLoopbackTypeIsValid(loopback_type) != NO_ERROR) { // to avoid using undefined loopback type & ref/dual mic in single mic project
        ALOGW("-%s(): No such Loopback type %d", __FUNCTION__, loopback_type);
        return BAD_TYPE;
    }

    // lock
    AudioResourceManagerInterface *pAudioResourceManager = AudioResourceManagerFactory::CreateAudioResource();
    pAudioResourceManager->EnableAudioLock(AudioResourceManagerInterface::AUDIO_HARDWARE_LOCK, 3000);
    pAudioResourceManager->EnableAudioLock(AudioResourceManagerInterface::AUDIO_MODE_LOCK, 3000);
    pAudioResourceManager->EnableAudioLock(AudioResourceManagerInterface::AUDIO_VOLUME_LOCK, 3000);

    // force all input/output streams standby
    AudioMTKStreamManager::getInstance()->ForceAllStandby();

    // copy current device
    mInputDeviceCopy  = (audio_devices_t)pAudioResourceManager->getUlInputDevice();
    mOutputDeviceCopy = (audio_devices_t)pAudioResourceManager->getDlOutputDevice();

    // get loopback device
    audio_devices_t input_device  = GetInputDeviceByLoopbackType(loopback_type);
    audio_devices_t output_device = GetOutputDeviceByLoopbackType(loopback_type, loopback_output_device);

    // check modem status
    SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex);
    if (pSpeechDriver->CheckModemIsReady() == false) { // modem is sleep...
        for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++) { // get working modem index
            pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex((modem_index_t)modem_index);
            if (pSpeechDriver != NULL && pSpeechDriver->CheckModemIsReady() == true) {
                mWorkingModemIndex = (modem_index_t)modem_index;
                break;
            }
        }
    }

    // to avoid BT test being interferenced by modem side speech enhancement
    if (loopback_type == MD_BT_LOOPBACK) {
        SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex)->SetSpeechEnhancement(false);
    }

    // to turn on/off DMNR
    if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR ||
        loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR) {
        mMaskCopy = SpeechEnhancementController::GetInstance()->GetSpeechEnhancementMask(); // copy DMNR mask
        sph_enh_mask_struct_t mask = mMaskCopy;
        if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR) {
            mask.dynamic_func &= (~SPH_ENH_DYNAMIC_MASK_DMNR);
        }
        else if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR) {
            mask.dynamic_func |= SPH_ENH_DYNAMIC_MASK_DMNR;
        }
        SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex)->SetSpeechEnhancementMask(mask);
    }

    // Enable loopback function
    switch (loopback_type) {
        case AP_MAIN_MIC_AFE_LOOPBACK:
        case AP_HEADSET_MIC_AFE_LOOPBACK:
        case AP_REF_MIC_AFE_LOOPBACK:
        case AP_BT_LOOPBACK: {
            AudioLoopbackController::GetInstance()->OpenAudioLoopbackControlFlow(input_device, output_device);
            break;
        }
        case MD_MAIN_MIC_ACOUSTIC_LOOPBACK:
        case MD_HEADSET_MIC_ACOUSTIC_LOOPBACK:
        case MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR:
        case MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR:
        case MD_REF_MIC_ACOUSTIC_LOOPBACK:
        case MD_BT_LOOPBACK: {
            SpeechLoopbackController::GetInstance()->OpenModemLoopbackControlFlow(mWorkingModemIndex, input_device, output_device);
            break;
        }
        default: {
            ALOGW("%s(): Loopback type %d not implemented!!", __FUNCTION__, loopback_type);
            ASSERT(0);
        }
    }

    // only use L ch data, so mute R ch. (Disconnect ADC_I2S_IN_R -> MODEM_PCM_TX_R)
    if (loopback_type == MD_MAIN_MIC_ACOUSTIC_LOOPBACK ||
        loopback_type == MD_REF_MIC_ACOUSTIC_LOOPBACK) {
        AudioDigitalControlFactory::CreateAudioDigitalControl()->SetinputConnection(
            AudioDigitalType::DisConnect,
            AudioDigitalType::I04,
            (mWorkingModemIndex == MODEM_1) ? AudioDigitalType::O18 : AudioDigitalType::O08);
    }

    // save opened loobpack type
    mLoopbackType = loopback_type;

    // acquire wake lock
    int ret = acquire_wake_lock(PARTIAL_WAKE_LOCK, LOOPBACK_WAKELOCK_NAME);
    ALOGD("%s(), acquire_wake_lock:%s, return %d.", __FUNCTION__, LOOPBACK_WAKELOCK_NAME, ret);

    // unlock
    pAudioResourceManager->DisableAudioLock(AudioResourceManagerInterface::AUDIO_VOLUME_LOCK);
    pAudioResourceManager->DisableAudioLock(AudioResourceManagerInterface::AUDIO_MODE_LOCK);
    pAudioResourceManager->DisableAudioLock(AudioResourceManagerInterface::AUDIO_HARDWARE_LOCK);

    ALOGD("-%s(), loopback_type = %d, loopback_output_device = %d", __FUNCTION__,  loopback_type, loopback_output_device);
    return NO_ERROR;
}
void *SpeechVMRecorder::DumpVMRecordDataThread(void *arg)
{
    // Adjust thread priority
    prctl(PR_SET_NAME, (unsigned long)__FUNCTION__, 0, 0, 0);
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);

    ALOGD("%s(), pid: %d, tid: %d", __FUNCTION__, getpid(), gettid());

    SpeechVMRecorder *pSpeechVMRecorder = (SpeechVMRecorder *)arg;
    RingBuf &ring_buf = pSpeechVMRecorder->mRingBuf;

    // open modem record function
    SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriver();
    status_t retval = pSpeechDriver->VoiceMemoRecordOn();
    if (retval != NO_ERROR)
    {
        ALOGE("%s(), VoiceMemoRecordOn() fail!! Return.", __FUNCTION__);
        pSpeechDriver->VoiceMemoRecordOff();
        pthread_exit(NULL);
        return 0;
    }

    // open file
    if (pSpeechVMRecorder->OpenFile() != NO_ERROR)
    {
        pSpeechDriver->VoiceMemoRecordOff();
        pthread_exit(NULL);
        return 0;
    }

    // Internal Input Buffer Initialization
    pSpeechVMRecorder->mRingBuf.pBufBase = new char[kReadBufferSize];
    pSpeechVMRecorder->mRingBuf.bufLen   = kReadBufferSize;
    pSpeechVMRecorder->mRingBuf.pRead    = pSpeechVMRecorder->mRingBuf.pBufBase;
    pSpeechVMRecorder->mRingBuf.pWrite   = pSpeechVMRecorder->mRingBuf.pBufBase;

    ASSERT(pSpeechVMRecorder->mRingBuf.pBufBase != NULL);
    memset(pSpeechVMRecorder->mRingBuf.pBufBase, 0, pSpeechVMRecorder->mRingBuf.bufLen);

    pSpeechVMRecorder->mStarting = true;

    while (1)
    {
        // lock & wait data
        pthread_mutex_lock(&pSpeechVMRecorder->mMutex);
        int ret = pthread_cond_timeout_np(&pSpeechVMRecorder->mExitCond, &pSpeechVMRecorder->mMutex, kCondWaitTimeoutMsec);
        if (ret != 0)
        {
            ALOGW("%s(), pthread_cond_timeout_np return %d. ", __FUNCTION__, ret);
        }

        // make sure VM is still recording after conditional wait
        if (pSpeechVMRecorder->mStarting == false)
        {

            // close file
            if (pSpeechVMRecorder->mDumpFile != NULL)
            {
                fflush(pSpeechVMRecorder->mDumpFile);
                fclose(pSpeechVMRecorder->mDumpFile);
                pSpeechVMRecorder->mDumpFile = NULL;
            }

            // release local ring buffer
            if (pSpeechVMRecorder->mRingBuf.pBufBase != NULL)
            {
                delete []pSpeechVMRecorder->mRingBuf.pBufBase;
                pSpeechVMRecorder->mRingBuf.pBufBase = NULL;
                pSpeechVMRecorder->mRingBuf.pRead    = NULL;
                pSpeechVMRecorder->mRingBuf.pWrite   = NULL;
                pSpeechVMRecorder->mRingBuf.bufLen   = 0;
            }

            ALOGD("%s(), pid: %d, tid: %d, mStarting == false, break", __FUNCTION__, getpid(), gettid());
            pthread_mutex_unlock(&pSpeechVMRecorder->mMutex);
            break;
        }

        // write data to sd card
        const uint16_t data_count = RingBuf_getDataCount(&ring_buf);
        uint16_t write_bytes = 0;

        if (data_count > 0)
        {
            const char *end = ring_buf.pBufBase + ring_buf.bufLen;
            if (ring_buf.pRead <= ring_buf.pWrite)
            {
                write_bytes += fwrite((void *)ring_buf.pRead, sizeof(char), data_count, pSpeechVMRecorder->mDumpFile);
            }
            else
            {
                int r2e = end - ring_buf.pRead;
                write_bytes += fwrite((void *)ring_buf.pRead, sizeof(char), r2e, pSpeechVMRecorder->mDumpFile);
                write_bytes += fwrite((void *)ring_buf.pBufBase, sizeof(char), data_count - r2e, pSpeechVMRecorder->mDumpFile);
            }

            ring_buf.pRead += write_bytes;
            if (ring_buf.pRead >= end) { ring_buf.pRead -= ring_buf.bufLen; }

            SLOGV("data_count: %u, write_bytes: %u", data_count, write_bytes);
        }

        if (write_bytes != data_count)
        {
            ALOGE("%s(), write_bytes(%d) != data_count(%d), SD Card might be full!!", __FUNCTION__, write_bytes, data_count);
        }

        // unlock
        pthread_mutex_unlock(&pSpeechVMRecorder->mMutex);
    }

    pthread_exit(NULL);
    return 0;
}
status_t LoopbackManager::SetLoopbackOn(loopback_t loopback_type, loopback_output_device_t loopback_output_device)
{
    ALOGD("+%s(), loopback_type = %d, loopback_output_device = %d", __FUNCTION__,  loopback_type, loopback_output_device);

    Mutex::Autolock _l(mLock);

    if (mLoopbackType != NO_LOOPBACK) // check no loobpack function on
    {
        ALOGD("-%s() : Please Turn off Loopback Type %d First!!", __FUNCTION__, mLoopbackType);
        return ALREADY_EXISTS;
    }
    else if (CheckLoopbackTypeIsValid(loopback_type) != NO_ERROR) // to avoid using undefined loopback type & ref/dual mic in single mic project
    {
        ALOGW("-%s(): No such Loopback type %d", __FUNCTION__, loopback_type);
        return BAD_TYPE;
    }


    // suspend & standby all input/output streams
    AudioALSAStreamManager::getInstance()->setAllStreamsSuspend(true);
    AudioALSAStreamManager::getInstance()->standbyAllStreams();


    // copy current device // TODO(Harvey): recover device
    //mInputDeviceCopy  = (audio_devices_t)pAudioResourceManager->getUlInputDevice();
    //mOutputDeviceCopy = (audio_devices_t)pAudioResourceManager->getDlOutputDevice();

    // get loopback device
    audio_devices_t input_device  = GetInputDeviceByLoopbackType(loopback_type);
    audio_devices_t output_device = GetOutputDeviceByLoopbackType(loopback_type, loopback_output_device);

    // set specific mic type
    if (loopback_type == AP_MAIN_MIC_AFE_LOOPBACK || loopback_type == MD_MAIN_MIC_ACOUSTIC_LOOPBACK)
    {
        AudioALSAHardwareResourceManager::getInstance()->setBuiltInMicSpecificType(BUILTIN_MIC_MIC1_ONLY);
    }
    else if (loopback_type == AP_REF_MIC_AFE_LOOPBACK || loopback_type == MD_REF_MIC_ACOUSTIC_LOOPBACK)
    {
        AudioALSAHardwareResourceManager::getInstance()->setBuiltInMicSpecificType(BUILTIN_MIC_MIC2_ONLY);
    }    
    else if (loopback_type == AP_3RD_MIC_AFE_LOOPBACK || loopback_type == MD_3RD_MIC_ACOUSTIC_LOOPBACK)
    {
        AudioALSAHardwareResourceManager::getInstance()->setBuiltInMicSpecificType(BUILTIN_MIC_MIC3_ONLY);
    }


    // check modem status
    if (CheckIsModemLoopback(loopback_type) == true)
    {
        SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex);
        if (pSpeechDriver->CheckModemIsReady() == false) // modem is sleep...
        {
            for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++) // get working modem index
            {
                pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex((modem_index_t)modem_index);
                if (pSpeechDriver != NULL && pSpeechDriver->CheckModemIsReady() == true)
                {
                    mWorkingModemIndex = (modem_index_t)modem_index;
                    SpeechDriverFactory::GetInstance()->SetActiveModemIndex(mWorkingModemIndex);
                    break;
                }
            }
        }
    }

    // to avoid BT test being interferenced by modem side speech enhancement
    mBtHeadsetNrecOnCopy = SpeechEnhancementController::GetInstance()->GetBtHeadsetNrecOn();
    if (loopback_type == MD_BT_LOOPBACK || loopback_type == MD_BT_LOOPBACK_NO_CODEC)
    {
        SpeechEnhancementController::GetInstance()->SetBtHeadsetNrecOnToAllModem(false);
    }

    // to turn on/off DMNR
    if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR ||
        loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
    {
        mMaskCopy = SpeechEnhancementController::GetInstance()->GetSpeechEnhancementMask(); // copy DMNR mask
        sph_enh_mask_struct_t mask = mMaskCopy;
        if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR)
        {
            mask.dynamic_func &= (~SPH_ENH_DYNAMIC_MASK_DMNR);
        }
        else if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
        {
            mask.dynamic_func |= SPH_ENH_DYNAMIC_MASK_DMNR;
        }
        SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex)->SetSpeechEnhancementMask(mask);
    }
    
    // BT CVSD
    if (loopback_type == AP_BT_LOOPBACK)
    {
        AudioALSALoopbackController::getInstance()->SetApBTCodec(true);
        AudioALSALoopbackController::getInstance()->OpenAudioLoopbackControlFlow(input_device, output_device);
    }
    else if (loopback_type == AP_BT_LOOPBACK_NO_CODEC)
    {
        AudioALSALoopbackController::getInstance()->SetApBTCodec(false);
        AudioALSALoopbackController::getInstance()->OpenAudioLoopbackControlFlow(input_device, output_device);        
    }
    else if (loopback_type == MD_BT_LOOPBACK)
    {
        AudioALSASpeechLoopbackController::getInstance()->SetModemBTCodec(true);
        AudioALSASpeechLoopbackController::getInstance()->OpenModemLoopbackControlFlow(input_device, output_device);        
    }
    else if (loopback_type == MD_BT_LOOPBACK_NO_CODEC)
    {
        AudioALSASpeechLoopbackController::getInstance()->SetModemBTCodec(false);
        AudioALSASpeechLoopbackController::getInstance()->OpenModemLoopbackControlFlow(input_device, output_device);        
    }
    else
    {
        // Enable loopback function
        switch (loopback_type)
        {
            case AP_MAIN_MIC_AFE_LOOPBACK:
            case AP_HEADSET_MIC_AFE_LOOPBACK:
            case AP_REF_MIC_AFE_LOOPBACK:
            case AP_3RD_MIC_AFE_LOOPBACK:
            //case AP_BT_LOOPBACK:
            //case AP_BT_LOOPBACK_NO_CODEC:
            {
                AudioALSALoopbackController::getInstance()->open(output_device, input_device);
                break;
            }
            case MD_MAIN_MIC_ACOUSTIC_LOOPBACK:
            case MD_HEADSET_MIC_ACOUSTIC_LOOPBACK:
            case MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR:
            case MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR:
            case MD_REF_MIC_ACOUSTIC_LOOPBACK:
            case MD_3RD_MIC_ACOUSTIC_LOOPBACK:
            //case MD_BT_LOOPBACK:
            //case MD_BT_LOOPBACK_NO_CODEC:
            {
#if defined(MTK_AUDIO_GAIN_TABLE)&&defined(MTK_AUDIO_SPH_LPBK_PARAM)
                AudioALSAStreamManager::getInstance()->UpdateSpeechLpbkParams();
#endif
                AudioALSASpeechLoopbackController::getInstance()->open(output_device, input_device);
                break;
            }
            default:
            {
                ALOGW("%s(): Loopback type %d not implemented!!", __FUNCTION__, loopback_type);
                ASSERT(0);
            }
        }
    }
    /*
        // only use L ch data, so mute R ch. (Disconnect ADC_I2S_IN_R -> MODEM_PCM_TX_R)
        if (loopback_type == MD_MAIN_MIC_ACOUSTIC_LOOPBACK ||
            loopback_type == MD_REF_MIC_ACOUSTIC_LOOPBACK)
        {
            AudioDigitalControlFactory::CreateAudioDigitalControl()->SetinputConnection(
                AudioDigitalType::DisConnect,
                AudioDigitalType::I04,
                (mWorkingModemIndex == MODEM_1) ? AudioDigitalType::O18 : AudioDigitalType::O08);
        }
    */

    // save opened loobpack type
    mLoopbackType = loopback_type;

    // acquire wake lock
    int ret = acquire_wake_lock(PARTIAL_WAKE_LOCK, LOOPBACK_WAKELOCK_NAME);
    ALOGD("%s(), acquire_wake_lock:%s, return %d.", __FUNCTION__, LOOPBACK_WAKELOCK_NAME, ret);

    // Volume
    if ((loopback_type != AP_BT_LOOPBACK) && (loopback_type != AP_BT_LOOPBACK_NO_CODEC) && (loopback_type != MD_BT_LOOPBACK) && (loopback_type != MD_BT_LOOPBACK_NO_CODEC))
    {
        if (CheckIsModemLoopback(loopback_type) == true)
        {
            mVoiceVolumeCopy = mAudioALSAVolumeController->getVoiceVolume();
            mAudioALSAVolumeController->setVoiceVolume(kVoiceVolumeForLoopback, AUDIO_MODE_IN_CALL, output_device);
        }
        else
        {
            mMasterVolumeCopy = mAudioALSAVolumeController->getMasterVolume();
            mAudioALSAVolumeController->setMasterVolume(kMasterVolumeForLoopback, AUDIO_MODE_NORMAL, output_device);
        }
    }
    ALOGD("-%s(), loopback_type = %d, loopback_output_device = %d", __FUNCTION__,  loopback_type, loopback_output_device);
    return NO_ERROR;
}
status_t SpeechPhoneCallController::ChangeDeviceForModemSpeechControlFlow(const audio_mode_t audio_mode, const audio_devices_t new_device)
{
    Mutex::Autolock _l(mLock);

    ALOGD("+%s(), audio_mode = %d, new_device = 0x%x", __FUNCTION__, audio_mode, new_device);

    const modem_index_t modem_index = mSpeechDriverFactory->GetActiveModemIndex();
    ASSERT((modem_index == MODEM_1 && audio_mode == AUDIO_MODE_IN_CALL) ||
           (modem_index == MODEM_2 && audio_mode == AUDIO_MODE_IN_CALL_2));

    // Get current active speech driver
    SpeechDriverInterface *pSpeechDriver = mSpeechDriverFactory->GetSpeechDriver();


    // Mute during device change.
    pSpeechDriver->SetDownlinkMute(true);
    pSpeechDriver->SetUplinkMute(true);


    // Stop PMIC digital/analog part - downlink
    mAudioResourceManager->StopOutputDevice();

    // Stop Side Tone Filter
    mAudioDigitalInstance->EnableSideToneFilter(false);

    // Stop MODEM_PCM
    mAudioDigitalInstance->SetModemPcmEnable(modem_index, false);

    // Stop PMIC digital/analog part - uplink
    mAudioResourceManager->StopInputDevice();

    // Stop AP side digital part
    CloseModemSpeechDigitalPart(modem_index, (audio_devices_t)mAudioResourceManager->getDlOutputDevice());



    // Set new device
    if (CheckTtyNeedOn() == true)
    {
        SetTtyInOutDevice(GetRoutingForTty(), mTty_Ctm, audio_mode);
    }
    else
    {
        mAudioResourceManager->setDlOutputDevice(new_device);
    }

    // Get new device
    const audio_devices_t output_device = (audio_devices_t)mAudioResourceManager->getDlOutputDevice();
    const audio_devices_t input_device  = (audio_devices_t)mAudioResourceManager->getUlInputDevice();
    ALOGD("%s(), output_device = 0x%x, input_device = 0x%x", __FUNCTION__, output_device, input_device);



    // Check BT device
    const bool bt_device_on = android_audio_legacy::AudioSystem::isBluetoothScoDevice((android_audio_legacy::AudioSystem::audio_devices)output_device);
#if 1
    int sample_rate;
    if (bt_device_on == true)
    {
        if (mBTMode == 0) //NB BTSCO
        {
            sample_rate = 8000;
        }
        else
        {
            sample_rate = 16000;
        }
    }
    else
    {
        sample_rate = 16000;
    }
    ALOGD("+%s(), bt_device_on = %d, sample_rate = %d", __FUNCTION__, bt_device_on, sample_rate);
#else
    const int  sample_rate  = (bt_device_on == true) ? 8000 : 16000; // TODO: MT6628 BT only use NB
#endif

    // Set sampling rate
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_OUT_DAC, sample_rate);
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_IN_ADC,  sample_rate);

    // Open ADC/DAC I2S, or DAIBT
    OpenModemSpeechDigitalPart(modem_index, output_device);



    // Clean Side Tone Filter gain
    pSpeechDriver->SetSidetoneGain(0);

    // Set PMIC digital/analog part - uplink has pop, open first
    mAudioResourceManager->StartInputDevice();
    if (bt_device_on == false) { usleep(kDelayForUplinkPulseMs * 1000); } // PMIC HW pulse
    // Set PMIC digital/analog part - DL need trim code.
    mAudioResourceManager->StartOutputDevice(); // also set volume here

    // start Side Tone Filter
    if (CheckSideToneFilterNeedOn(output_device) == true)
    {
        mAudioDigitalInstance->EnableSideToneFilter(true);
    }



    // Set MODEM_PCM - open modem pcm here s.t. modem/DSP can learn the uplink background noise, but not zero
    SetModemPcmAttribute(modem_index, sample_rate);
    mAudioDigitalInstance->SetModemPcmEnable(modem_index, true);


    // Set MD side sampling rate
    pSpeechDriver->SetModemSideSamplingRate(sample_rate);

    // Set speech mode
    pSpeechDriver->SetSpeechMode(input_device, output_device);

    // Need recover mute state
    pSpeechDriver->SetUplinkMute(mMicMute);
    pSpeechDriver->SetDownlinkMute(false);

    ALOGD("-%s(), audio_mode = %d", __FUNCTION__, audio_mode);
    return NO_ERROR;
}
status_t SpeechPhoneCallController::CloseModemSpeechControlFlow(const audio_mode_t audio_mode)
{
    Mutex::Autolock _l(mLock);

    ALOGD("+%s(), audio_mode = %d", __FUNCTION__, audio_mode);

    const modem_index_t modem_index = mSpeechDriverFactory->GetActiveModemIndex();
    ASSERT((modem_index == MODEM_1 && audio_mode == AUDIO_MODE_IN_CALL) ||
           (modem_index == MODEM_2 && audio_mode == AUDIO_MODE_IN_CALL_2));

    // check VM need close
    SpeechVMRecorder *pSpeechVMRecorder = SpeechVMRecorder::GetInstance();
    if (pSpeechVMRecorder->GetVMRecordStatus() == true)
    {
        ALOGD("%s(), Close VM/EPL record", __FUNCTION__);
        pSpeechVMRecorder->Close();
    }

    // Stop PMIC digital/analog part - downlink
    mAudioResourceManager->StopOutputDevice();

    // Stop Side Tone Filter
    mAudioDigitalInstance->EnableSideToneFilter(false);

    // Stop MODEM_PCM
    mAudioDigitalInstance->SetModemPcmEnable(modem_index, false);

    // Stop PMIC digital/analog part - uplink
    mAudioResourceManager->StopInputDevice();

    // Stop AP side digital part
    CloseModemSpeechDigitalPart(modem_index, (audio_devices_t)mAudioResourceManager->getDlOutputDevice());



    // Get current active speech driver
    SpeechDriverInterface *pSpeechDriver = mSpeechDriverFactory->GetSpeechDriver();

    // check BGS need close
    if (pSpeechDriver->GetApSideModemStatus(BGS_STATUS_MASK) == true)
    {
        pSpeechDriver->BGSoundOff();
    }

    // Speech/VT off
    if (pSpeechDriver->GetApSideModemStatus(VT_STATUS_MASK) == true)
    {
        pSpeechDriver->PCM2WayOff();
        pSpeechDriver->VideoTelephonyOff();
    }
    else if (pSpeechDriver->GetApSideModemStatus(SPEECH_STATUS_MASK) == true)
    {
        if (pSpeechDriver->GetApSideModemStatus(TTY_STATUS_MASK) == true)
        {
            pSpeechDriver->TtyCtmOff();
        }
        pSpeechDriver->SpeechOff();
    }
    else
    {
        ALOGE("%s(), audio_mode = %d, Speech & VT are already closed!!", __FUNCTION__, audio_mode);
        ASSERT(pSpeechDriver->GetApSideModemStatus(VT_STATUS_MASK)     == true ||
               pSpeechDriver->GetApSideModemStatus(SPEECH_STATUS_MASK) == true);
    }

    // AFE_ON = false
    mAudioDigitalInstance->SetAfeEnable(false);

    // recover sampling rate
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_OUT_DAC, 44100);
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_IN_ADC, 44100);

    // disable clock
    SetAfeAnalogClock(false);

    // clean VT status
    if (mVtNeedOn == true)
    {
        ALOGD("%s(), Set mVtNeedOn = false");
        mVtNeedOn = false;
    }

    ALOGD("-%s(), audio_mode = %d", __FUNCTION__, audio_mode);

    return NO_ERROR;
}
status_t SpeechPhoneCallController::OpenModemSpeechControlFlow(const audio_mode_t audio_mode)
{
    Mutex::Autolock _l(mLock);

    ALOGD("+%s(), audio_mode = %d", __FUNCTION__, audio_mode);

    if (IsModeIncall(audio_mode) == false)
    {
        ALOGE("-%s() new_mode(%d) != MODE_IN_CALL / MODE_IN_CALL_2", __FUNCTION__, audio_mode);
        return INVALID_OPERATION;
    }

    // get speech driver instance
    mSpeechDriverFactory->SetActiveModemIndexByAudioMode(audio_mode);
    const modem_index_t    modem_index   = mSpeechDriverFactory->GetActiveModemIndex();
    SpeechDriverInterface *pSpeechDriver = mSpeechDriverFactory->GetSpeechDriver();

    // check BT device
    const bool bt_device_on = android_audio_legacy::AudioSystem::isBluetoothScoDevice((android_audio_legacy::AudioSystem::audio_devices)mAudioResourceManager->getDlOutputDevice());

#if 1
    int sample_rate;
    if (bt_device_on == true)
    {
        if (mBTMode == 0) //NB BTSCO
        {
            sample_rate = 8000;
        }
        else
        {
            sample_rate = 16000;
        }
    }
    else
    {
        sample_rate = 16000;
    }
    ALOGD("+%s(), bt_device_on = %d, sample_rate = %d", __FUNCTION__, bt_device_on, sample_rate);
#else
    const int  sample_rate  = (bt_device_on == true) ? 8000 : 16000; // TODO: MT6628 BT only use NB
#endif

    // enable clock
    SetAfeAnalogClock(true);

    // set sampling rate
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_OUT_DAC, sample_rate);
    mAudioAnalogInstance->SetFrequency(AudioAnalogType::DEVICE_IN_ADC,  sample_rate);

    // set device
    if (CheckTtyNeedOn() == true)
    {
        SetTtyInOutDevice(GetRoutingForTty(), mTty_Ctm, audio_mode);
    }
    else
    {
        // Note: set output device in phone call will also assign input device
        mAudioResourceManager->setDlOutputDevice(mAudioResourceManager->getDlOutputDevice());
    }

    // get device
    const audio_devices_t output_device = (audio_devices_t)mAudioResourceManager->getDlOutputDevice();
    const audio_devices_t input_device  = (audio_devices_t)mAudioResourceManager->getUlInputDevice();
    ALOGD("%s(), output_device = 0x%x, input_device = 0x%x", __FUNCTION__, output_device, input_device);

    // Open ADC/DAC I2S, or DAIBT
    OpenModemSpeechDigitalPart(modem_index, output_device);

    // AFE_ON
    mAudioDigitalInstance->SetAfeEnable(true);



    // Clean Side Tone Filter gain
    pSpeechDriver->SetSidetoneGain(0);

    // Set PMIC digital/analog part - uplink has pop, open first
    mAudioResourceManager->StartInputDevice();
    if (bt_device_on == false) { usleep(kDelayForUplinkPulseMs * 1000); } // PMIC HW pulse

    // set MODEM_PCM - open modem pcm here s.t. modem/DSP can learn the uplink background noise, but not zero
    SetModemPcmAttribute(modem_index, sample_rate);
    mAudioDigitalInstance->SetModemPcmEnable(modem_index, true);

    // Set MD side sampling rate
    pSpeechDriver->SetModemSideSamplingRate(sample_rate);

    // Set speech mode
    pSpeechDriver->SetSpeechMode(input_device, output_device);

    // Speech/VT on
    if (mVtNeedOn == true)
    {
        pSpeechDriver->VideoTelephonyOn();

        // trun on P2W for Video Telephony
        bool wideband_on = false; // VT default use Narrow Band (8k), modem side will SRC to 16K
        pSpeechDriver->PCM2WayOn(wideband_on);
    }
    else
    {
        pSpeechDriver->SpeechOn();

        // turn on TTY
        if (CheckTtyNeedOn() == true)
        {
            pSpeechDriver->TtyCtmOn(BAUDOT_MODE);
        }
    }

    // Set PMIC digital/analog part - DL need trim code.
    mAudioResourceManager->StartOutputDevice();

    // start Side Tone Filter
    if (CheckSideToneFilterNeedOn(output_device) == true)
    {
        mAudioDigitalInstance->EnableSideToneFilter(true);
    }

    // check VM need open
    SpeechVMRecorder *pSpeechVMRecorder = SpeechVMRecorder::GetInstance();
    if (pSpeechVMRecorder->GetVMRecordCapability() == true)
    {
        ALOGD("%s(), Open VM/EPL record", __FUNCTION__);
        pSpeechVMRecorder->Open();
    }

    ALOGD("-%s(), audio_mode = %d", __FUNCTION__, audio_mode);
    return NO_ERROR;
}