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