/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <time.h>
#include <sys/time.h>

#include "audio_device_utility.h"
#include "audio_device_android_opensles.h"
#include "audio_device_config.h"

#include "trace.h"
#include "thread_wrapper.h"
#include "event_wrapper.h"

#ifdef WEBRTC_ANDROID_DEBUG
#include <android/log.h>
#define WEBRTC_TRACE(a,b,c,...)  __android_log_print(                  \
           ANDROID_LOG_DEBUG, "WebRTC ADM OpenSLES", __VA_ARGS__)
#endif

namespace webrtc {

// ============================================================================
//                            Construction & Destruction
// ============================================================================

// ----------------------------------------------------------------------------
//  AudioDeviceAndroidOpenSLES - ctor
// ----------------------------------------------------------------------------

AudioDeviceAndroidOpenSLES::AudioDeviceAndroidOpenSLES(const WebRtc_Word32 id) :
    _ptrAudioBuffer(NULL),
    _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
    _id(id),
    _slEngineObject(NULL),
    _slPlayer(NULL),
    _slEngine(NULL),
    _slPlayerPlay(NULL),
    _slOutputMixObject(NULL),
    _slSpeakerVolume(NULL),
    _slRecorder(NULL),
    _slRecorderRecord(NULL),
    _slAudioIODeviceCapabilities(NULL),
    _slRecorderSimpleBufferQueue(NULL),
    _slMicVolume(NULL),
    _micDeviceId(0),
    _recQueueSeq(0),
    _timeEventRec(*EventWrapper::Create()),
    _ptrThreadRec(NULL),
    _recThreadID(0),
    _playQueueSeq(0),
    _recCurrentSeq(0),
    _recBufferTotalSize(0),
    _recordingDeviceIsSpecified(false),
    _playoutDeviceIsSpecified(false),
    _initialized(false),
    _recording(false),
    _playing(false),
    _recIsInitialized(false),
    _playIsInitialized(false),
    _micIsInitialized(false),
    _speakerIsInitialized(false),
    _playWarning(0),
    _playError(0),
    _recWarning(0),
    _recError(0),
    _playoutDelay(0),
    _recordingDelay(0),
    _AGC(false),
    _adbSampleRate(0),
    _samplingRateIn(SL_SAMPLINGRATE_16),
    _samplingRateOut(SL_SAMPLINGRATE_16),
    _maxSpeakerVolume(0),
    _minSpeakerVolume(0),
    _loudSpeakerOn(false) {
    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created",
                 __FUNCTION__);
    memset(_playQueueBuffer, 0, sizeof(_playQueueBuffer));
    memset(_recQueueBuffer, 0, sizeof(_recQueueBuffer));
    memset(_recBuffer, 0, sizeof(_recBuffer));
    memset(_recLength, 0, sizeof(_recLength));
    memset(_recSeqNumber, 0, sizeof(_recSeqNumber));
}

AudioDeviceAndroidOpenSLES::~AudioDeviceAndroidOpenSLES() {
    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed",
                 __FUNCTION__);

    Terminate();

    delete &_timeEventRec;
    delete &_critSect;
}

// ============================================================================
//                                     API
// ============================================================================

void AudioDeviceAndroidOpenSLES::AttachAudioBuffer(
    AudioDeviceBuffer* audioBuffer) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    _ptrAudioBuffer = audioBuffer;

    // inform the AudioBuffer about default settings for this implementation
    _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
    _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
    _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS);
    _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS);
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::ActiveAudioLayer(
    AudioDeviceModule::AudioLayer& audioLayer) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    audioLayer = AudioDeviceModule::kPlatformDefaultAudio;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::Init() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (_initialized) {
        return 0;
    }

    _playWarning = 0;
    _playError = 0;
    _recWarning = 0;
    _recError = 0;

    SLEngineOption EngineOption[] = { (SLuint32) SL_ENGINEOPTION_THREADSAFE,
            (SLuint32) SL_BOOLEAN_TRUE };
    WebRtc_Word32 res = slCreateEngine(&_slEngineObject, 1, EngineOption, 0,
                                       NULL, NULL);
    //WebRtc_Word32 res = slCreateEngine( &_slEngineObject, 0, NULL, 0, NULL,
    //    NULL);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to create SL Engine Object");
        return -1;
    }
    /* Realizing the SL Engine in synchronous mode. */
    if ((*_slEngineObject)->Realize(_slEngineObject, SL_BOOLEAN_FALSE)
            != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to Realize SL Engine");
        return -1;
    }

    if ((*_slEngineObject)->GetInterface(_slEngineObject, SL_IID_ENGINE,
                                         (void*) &_slEngine)
            != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get SL Engine interface");
        return -1;
    }

    // Check the sample rate to be used for playback and recording
    if (InitSampleRate() != 0) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "%s: Failed to init samplerate", __FUNCTION__);
        return -1;
    }

    // Set the audio device buffer sampling rate, we assume we get the same
    // for play and record
    if (_ptrAudioBuffer->SetRecordingSampleRate(_adbSampleRate) < 0) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Could not set audio device buffer recording "
                         "sampling rate (%d)", _adbSampleRate);
    }
    if (_ptrAudioBuffer->SetPlayoutSampleRate(_adbSampleRate) < 0) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Could not set audio device buffer playout sampling "
                         "rate (%d)", _adbSampleRate);
    }

    _initialized = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::Terminate() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_initialized) {
        return 0;
    }

    // RECORDING
    StopRecording();

    _micIsInitialized = false;
    _recordingDeviceIsSpecified = false;

    // PLAYOUT
    StopPlayout();

    if (_slEngineObject != NULL) {
        (*_slEngineObject)->Destroy(_slEngineObject);
        _slEngineObject = NULL;
        _slEngine = NULL;
    }

    _initialized = false;

    return 0;
}

bool AudioDeviceAndroidOpenSLES::Initialized() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return (_initialized);
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerIsAvailable(bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    // We always assume it's available
    available = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitSpeaker() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (_playing) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  Playout already started");
        return -1;
    }

    if (!_playoutDeviceIsSpecified) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Playout device is not specified");
        return -1;
    }

    // Nothing needs to be done here, we use a flag to have consistent
    // behavior with other platforms
    _speakerIsInitialized = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneIsAvailable(bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    // We always assume it's available
    available = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitMicrophone() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (_recording) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  Recording already started");
        return -1;
    }

    if (!_recordingDeviceIsSpecified) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recording device is not specified");
        return -1;
    }

    // Nothing needs to be done here, we use a flag to have consistent
    // behavior with other platforms
    _micIsInitialized = true;

    return 0;
}

bool AudioDeviceAndroidOpenSLES::SpeakerIsInitialized() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _speakerIsInitialized;
}

bool AudioDeviceAndroidOpenSLES::MicrophoneIsInitialized() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _micIsInitialized;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolumeIsAvailable(
                                                                 bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = true; // We assume we are always be able to set/get volume

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetSpeakerVolume(
    WebRtc_UWord32 volume) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id,
                 "SetSpeakerVolume(volume=%u)", volume);

    if (!_speakerIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Speaker not initialized");
        return -1;
    }

    if (_slEngineObject == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "SetSpeakerVolume, SL Engine object doesnt exist");
        return -1;
    }

    if (_slEngine == NULL) {
        // Get the SL Engine Interface which is implicit
        if ((*_slEngineObject)->GetInterface(_slEngineObject, SL_IID_ENGINE,
                                             (void*) &_slEngine)
                != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to GetInterface SL Engine Interface");
            return -1;
        }
    }
    /*    if (_slOutputMixObject == NULL && _slEngine != NULL)
     {
     // Set arrays required[] and iidArray[] for VOLUME interface
     const SLInterfaceID ids[1] = {SL_IID_VOLUME};
     const SLboolean req[1] = {SL_BOOLEAN_TRUE};
     // Create Output Mix object to be used by player
     if ((*_slEngine)->CreateOutputMix(_slEngine, &_slOutputMixObject, 1, ids,
     req) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to create Output Mix object");
     return -1;
     }

     // Realizing the Output Mix object in synchronous mode.
     if ((*_slOutputMixObject)->Realize(_slOutputMixObject,
     SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to realize the output mix object");
     return -1;
     }
     }
     if (_slSpeakerVolume == NULL && _slOutputMixObject != NULL)
     {
     if ((*_slOutputMixObject)->GetInterface(_slOutputMixObject,
     SL_IID_VOLUME, (void*)&_slSpeakerVolume) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get interface for Speaker Volume");
     return -1;
     }
     }

     WebRtc_Word32 vol(0);
     vol = ((volume*(_maxSpeakerVolume-_minSpeakerVolume) + (int)(255/2))
     / (255)) + _minSpeakerVolume;
     if (_slSpeakerVolume != NULL)
     {
     if ((*_slSpeakerVolume)->SetVolumeLevel(_slSpeakerVolume, vol)
     != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to set speaker volume");
     return -1;
     }
     }
     */
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolume(
    WebRtc_UWord32& volume) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    /*    if (!_speakerIsInitialized)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  Speaker not initialized");
     return -1;
     }

     if (_slEngineObject == NULL)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "SetSpeakerVolume, SL Engine object doesnt exist");
     return -1;
     }

     if (_slEngine == NULL && _slEngineObject != NULL)
     {
     // Get the SL Engine Interface which is implicit
     if ((*_slEngineObject)->GetInterface(_slEngineObject,
     SL_IID_ENGINE, (void*)&_slEngine) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get SL Engine interface");
     return -1;
     }
     }
     if (_slOutputMixObject == NULL && _slEngine != NULL)
     {
     // Set arrays required[] and iidArray[] for VOLUME interface
     const SLInterfaceID ids[1] = {SL_IID_VOLUME};
     const SLboolean req[1] = {SL_BOOLEAN_TRUE};
     // Create Output Mix object to be used by player
     if ((*_slEngine)->CreateOutputMix(_slEngine,
     &_slOutputMixObject, 1, ids, req) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to create Output Mixer object");
     return -1;
     }
     // Realizing the Output Mix object in synchronous mode.
     if ((*_slOutputMixObject)->Realize(_slOutputMixObject,
     SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to realize Output Mix object");
     return -1;
     }
     }
     if (_slSpeakerVolume == NULL && _slOutputMixObject != NULL)
     {
     if ((*_slOutputMixObject)->GetInterface(_slOutputMixObject,
     SL_IID_VOLUME, (void*)&_slSpeakerVolume) != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get speaker volume interface");
     return -1;
     }
     }

     SLmillibel vol(0);
     if (_slSpeakerVolume != NULL)
     {
     if ((*_slSpeakerVolume)->GetVolumeLevel(_slSpeakerVolume, &vol)
     != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get speaker volume");
     return -1;
     }
     }
     // volume has to be mapped from millibel to [0, 255]
     //    volume = (WebRtc_UWord32) (((vol - _minSpeakerVolume) * 255 +
     * (int)((_maxSpeakerVolume - _minSpeakerVolume)/2)) /
     *  (_maxSpeakerVolume - _minSpeakerVolume));
     */
    return 0;
}

// ----------------------------------------------------------------------------
//  SetWaveOutVolume
// ----------------------------------------------------------------------------

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetWaveOutVolume(
    WebRtc_UWord16 /*volumeLeft*/,
    WebRtc_UWord16 /*volumeRight*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s)", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

// ----------------------------------------------------------------------------
//  WaveOutVolume
// ----------------------------------------------------------------------------

WebRtc_Word32 AudioDeviceAndroidOpenSLES::WaveOutVolume(
    WebRtc_UWord16& /*volumeLeft*/,
    WebRtc_UWord16& /*volumeRight*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MaxSpeakerVolume(
    WebRtc_UWord32& maxVolume) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (!_speakerIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Speaker not initialized");
        return -1;
    }

    maxVolume = _maxSpeakerVolume;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MinSpeakerVolume(
    WebRtc_UWord32& minVolume) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (!_speakerIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Speaker not initialized");
        return -1;//
    }

    minVolume = _minSpeakerVolume;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolumeStepSize(
    WebRtc_UWord16& stepSize) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (!_speakerIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Speaker not initialized");
        return -1;
    }
    stepSize = 1;

    return 0;
}

// ----------------------------------------------------------------------------
//  SpeakerMuteIsAvailable
// ----------------------------------------------------------------------------

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerMuteIsAvailable(
    bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false; // Speaker mute not supported on Android

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetSpeakerMute(bool /*enable*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerMute(bool& /*enabled*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneMuteIsAvailable(
    bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false; // Mic mute not supported on Android

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneMute(bool /*enable*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneMute(
    bool& /*enabled*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneBoostIsAvailable(
                                                                   bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false; // Mic boost not supported on Android

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneBoost(bool enable) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (!_micIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Microphone not initialized");
        return -1;
    }

    if (enable) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Enabling not available");
        return -1;
    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneBoost(bool& enabled) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (!_micIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Microphone not initialized");
        return -1;
    }

    enabled = false;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoRecordingIsAvailable(
    bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false; // Stereo recording not supported on Android

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetStereoRecording(bool enable) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (enable) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Enabling not available");
        return -1;
    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoRecording(bool& enabled) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    enabled = false;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoPlayoutIsAvailable(
    bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false; // Stereo playout not supported on Android

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetStereoPlayout(bool enable) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (enable) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Enabling not available");
        return -1;
    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoPlayout(bool& enabled) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    enabled = false;

    return 0;
}


WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetAGC(bool enable) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "SetAGC(enable=%d)",
                 enable);

    _AGC = enable;

    return 0;
}

bool AudioDeviceAndroidOpenSLES::AGC() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _AGC;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneVolumeIsAvailable(
    bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneVolume(
    WebRtc_UWord32 volume) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (_slEngineObject == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "SetMicrophoneVolume, SL Engine Object doesnt exist");
        return -1;
    }

    /* Get the optional DEVICE VOLUME interface from the engine */
    if (_slMicVolume == NULL) {
        // Get the optional DEVICE VOLUME interface from the engine
        if ((*_slEngineObject)->GetInterface(_slEngineObject,
                                             SL_IID_DEVICEVOLUME,
                                             (void*) &_slMicVolume)
                != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to create Output Mix object");
        }
    }

    if (_slMicVolume != NULL) {
        WebRtc_Word32 vol(0);
        vol = ((volume * (_maxSpeakerVolume - _minSpeakerVolume) + (int) (255
                / 2)) / (255)) + _minSpeakerVolume;
        if ((*_slMicVolume)->SetVolume(_slMicVolume, _micDeviceId, vol)
                != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to create Output Mix object");
        }
    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneVolume(
    WebRtc_UWord32& /*volume*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    /*    if (_slEngineObject == NULL)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "MicrophoneVolume, SL Engine Object doesnt exist");
     return -1;
     }

     // Get the optional DEVICE VOLUME interface from the engine
     if (_slMicVolume == NULL)
     {
     // Get the optional DEVICE VOLUME interface from the engine
     if ((*_slEngineObject)->GetInterface(_slEngineObject,
     SL_IID_DEVICEVOLUME, (void*)&_slMicVolume)  != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Microphone Volume interface");
     }
     }

     SLint32 vol(0);
     if (_slMicVolume != NULL)
     {
     if ((*_slMicVolume)->GetVolume(_slMicVolume, _micDeviceId, &vol)
     != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Microphone Volume");
     }
     }
     // volume has to be mapped from millibel to [0, 255]
     //    volume = (WebRtc_UWord32) (((vol - _minSpeakerVolume) * 255 +
     * (int)((_maxSpeakerVolume - _minSpeakerVolume)/2)) /
     *  (_maxSpeakerVolume - _minSpeakerVolume));
     */
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MaxMicrophoneVolume(
    WebRtc_UWord32& /*maxVolume*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MinMicrophoneVolume(
    WebRtc_UWord32& minVolume) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    minVolume = 0;
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneVolumeStepSize(
    WebRtc_UWord16& stepSize) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    stepSize = 1;
    return 0;
}

WebRtc_Word16 AudioDeviceAndroidOpenSLES::PlayoutDevices() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return 1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetPlayoutDevice(WebRtc_UWord16 index) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (_playIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Playout already initialized");
        return -1;
    }

    if (0 != index) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Device index is out of range [0,0]");
        return -1;
    }

    // Do nothing but set a flag, this is to have consistent behaviour
    // with other platforms
    _playoutDeviceIsSpecified = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetPlayoutDevice(
    AudioDeviceModule::WindowsDeviceType /*device*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutDeviceName(
    WebRtc_UWord16 index,
    WebRtc_Word8 name[kAdmMaxDeviceNameSize],
    WebRtc_Word8 guid[kAdmMaxGuidSize]) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (0 != index) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Device index is out of range [0,0]");
        return -1;
    }

    // Return empty string
    memset(name, 0, kAdmMaxDeviceNameSize);

    if (guid) {
        memset(guid, 0, kAdmMaxGuidSize);
    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::RecordingDeviceName(
    WebRtc_UWord16 index,
    WebRtc_Word8 name[kAdmMaxDeviceNameSize],
    WebRtc_Word8 guid[kAdmMaxGuidSize]) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (0 != index) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Device index is out of range [0,0]");
        return -1;
    }

    // Return empty string
    memset(name, 0, kAdmMaxDeviceNameSize);

    if (guid) {
        memset(guid, 0, kAdmMaxGuidSize);
    }

    return 0;
}

WebRtc_Word16 AudioDeviceAndroidOpenSLES::RecordingDevices() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return 1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetRecordingDevice(
    WebRtc_UWord16 index) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    if (_recIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recording already initialized");
        return -1;
    }

    if (0 != index) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Device index is out of range [0,0]");
        return -1;
    }

    // Do nothing but set a flag, this is to have consistent behaviour with
    // other platforms
    _recordingDeviceIsSpecified = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetRecordingDevice(
    AudioDeviceModule::WindowsDeviceType /*device*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutIsAvailable(bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false;

    // Try to initialize the playout side
    WebRtc_Word32 res = InitPlayout();

    // Cancel effect of initialization
    StopPlayout();

    if (res != -1) {
        available = true;
    }

    return res;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::RecordingIsAvailable(bool& available) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    available = false;

    // Try to initialize the playout side
    WebRtc_Word32 res = InitRecording();

    // Cancel effect of initialization
    StopRecording();

    if (res != -1) {
        available = true;
    }

    return res;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitPlayout() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_initialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  Not initialized");
        return -1;
    }

    if (_playing) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  Playout already started");
        return -1;
    }

    if (!_playoutDeviceIsSpecified) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Playout device is not specified");
        return -1;
    }

    if (_playIsInitialized) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Playout already initialized");
        return 0;
    }

    // Initialize the speaker
    if (InitSpeaker() == -1) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  InitSpeaker() failed");
    }

    if (_slEngineObject == NULL || _slEngine == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  SLObject or Engiine is NULL");
        return -1;
    }

    WebRtc_Word32 res(-1);
    SLDataFormat_PCM pcm;
    SLDataSource audioSource;
    SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue;
    SLDataSink audioSink;
    SLDataLocator_OutputMix locator_outputmix;

    // Create Output Mix object to be used by player
    SLInterfaceID ids[N_MAX_INTERFACES];
    SLboolean req[N_MAX_INTERFACES];
    for (unsigned int i = 0; i < N_MAX_INTERFACES; i++) {
        ids[i] = SL_IID_NULL;
        req[i] = SL_BOOLEAN_FALSE;
    }
    ids[0] = SL_IID_ENVIRONMENTALREVERB;
    res = (*_slEngine)->CreateOutputMix(_slEngine, &_slOutputMixObject, 1, ids,
                                        req);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get SL Output Mix object");
        return -1;
    }
    // Realizing the Output Mix object in synchronous mode.
    res = (*_slOutputMixObject)->Realize(_slOutputMixObject, SL_BOOLEAN_FALSE);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to realize SL Output Mix object");
        return -1;
    }
    // Get the speaker mixer
    /*    res = (*_slOutputMixObject)->GetInterface(_slOutputMixObject,
     * SL_IID_VOLUME, (void*)&_slSpeakerVolume);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Speaker Mixer");
     return -1;
     }
     */
    // The code below can be moved to startplayout instead
    /* Setup the data source structure for the buffer queue */
    simpleBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    /* Two buffers in our buffer queue, to have low latency*/
    simpleBufferQueue.numBuffers = N_PLAY_QUEUE_BUFFERS;
    // TODO(xians), figure out if we should support stereo playout for android
    /* Setup the format of the content in the buffer queue */
    pcm.formatType = SL_DATAFORMAT_PCM;
    pcm.numChannels = 1;
    // _samplingRateOut is initilized in InitSampleRate()
    pcm.samplesPerSec = SL_SAMPLINGRATE_16;
    pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
    pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    audioSource.pFormat = (void *) &pcm;
    audioSource.pLocator = (void *) &simpleBufferQueue;
    /* Setup the data sink structure */
    locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    locator_outputmix.outputMix = _slOutputMixObject;
    audioSink.pLocator = (void *) &locator_outputmix;
    audioSink.pFormat = NULL;

    // Set arrays required[] and iidArray[] for SEEK interface
    // (PlayItf is implicit)
    ids[0] = SL_IID_BUFFERQUEUE;
    ids[1] = SL_IID_EFFECTSEND;
    req[0] = SL_BOOLEAN_TRUE;
    req[1] = SL_BOOLEAN_TRUE;
    // Create the music player
    res = (*_slEngine)->CreateAudioPlayer(_slEngine, &_slPlayer, &audioSource,
                                          &audioSink, 2, ids, req);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to create Audio Player");
        return -1;
    }

    // Realizing the player in synchronous mode. 
    res = (*_slPlayer)->Realize(_slPlayer, SL_BOOLEAN_FALSE);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to realize the player");
        return -1;
    }
    // Get seek and play interfaces 
    res = (*_slPlayer)->GetInterface(_slPlayer, SL_IID_PLAY,
                                     (void*) &_slPlayerPlay);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get Player interface");
        return -1;
    }
    res = (*_slPlayer)->GetInterface(_slPlayer, SL_IID_BUFFERQUEUE,
                                     (void*) &_slPlayerSimpleBufferQueue);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get Player Simple Buffer Queue interface");
        return -1;
    }

    // Setup to receive buffer queue event callbacks
    res = (*_slPlayerSimpleBufferQueue)->RegisterCallback(
        _slPlayerSimpleBufferQueue,
        PlayerSimpleBufferQueueCallback,
        this);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to register Player Callback");
        return -1;
    }
    _playIsInitialized = true;
    return 0;
}

// ----------------------------------------------------------------------------
//  InitRecording
// ----------------------------------------------------------------------------

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitRecording() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_initialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  Not initialized");
        return -1;
    }

    if (_recording) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  Recording already started");
        return -1;
    }

    if (!_recordingDeviceIsSpecified) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recording device is not specified");
        return -1;
    }

    if (_recIsInitialized) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Recording already initialized");
        return 0;
    }

    // Initialize the microphone
    if (InitMicrophone() == -1) {
        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "  InitMicrophone() failed");
    }

    if (_slEngineObject == NULL || _slEngine == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recording object is NULL");
        return -1;
    }
    WebRtc_Word32 res(-1);
    WebRtc_Word32 numInputs(0);
    WebRtc_UWord32 inputDeviceIDs[N_MAX_INPUT_DEVICES];
    SLAudioInputDescriptor audioInputDescriptor;
    SLDataSource audioSource;
    SLDataLocator_IODevice micLocator;
    SLDataSink audioSink;
    SLDataFormat_PCM pcm;
    SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue;
    bool micAvailable(false);
    WebRtc_UWord32 micDeviceID(0);

    /*    // Get the Audio IO DEVICE CAPABILITIES interface, which is also implicit
     res = (*_slEngineObject)->GetInterface(_slEngineObject,
     SL_IID_AUDIOIODEVICECAPABILITIES, (void*)&_slAudioIODeviceCapabilities);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Audio IO device Capacilities interface");
     return -1;
     }
     numInputs = N_MAX_OUTPUT_DEVICES;

     res = (*_slAudioIODeviceCapabilities)->GetAvailableAudioInputs(
     _slAudioIODeviceCapabilities, &numInputs, inputDeviceIDs);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get the number of Input Devices");
     return -1;
     }

     // Search for either earpiece microphone or headset microphone input
     // device - with a preference for the latter
     for (int i=0;i<numInputs; i++)
     {
     res = (*_slAudioIODeviceCapabilities)->QueryAudioInputCapabilities(
     _slAudioIODeviceCapabilities, inputDeviceIDs[i], &audioInputDescriptor);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to query info for the Input Devices");
     return -1;
     }
     if((audioInputDescriptor.deviceConnection ==
     SL_DEVCONNECTION_ATTACHED_WIRED)
     && (audioInputDescriptor.deviceScope == SL_DEVSCOPE_USER)
     && (audioInputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET))
     {
     micDeviceID = inputDeviceIDs[i];
     micAvailable = true;
     break;
     }
     else if((audioInputDescriptor.deviceConnection ==
     SL_DEVCONNECTION_INTEGRATED)
     && (audioInputDescriptor.deviceScope == SL_DEVSCOPE_USER)
     && (audioInputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET))
     {
     micDeviceID = inputDeviceIDs[i];
     micAvailable = true;
     break;
     }
     }
     // If neither of the preferred input audio devices is available,
     // no point in continuing
     if (!micAvailable)
     {
     return -1;
     }
     */

    // Get the optional DEVICE VOLUME interface from the engine,
    // should this be done somewhere else
    /*    res = (*_slEngineObject)->GetInterface(_slEngineObject,
     * SL_IID_DEVICEVOLUME, (void*)&_slMicVolume);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Microphone Volume interface");
     return -1;
     }
     */
    // Setup the data source structure
    micLocator.locatorType = SL_DATALOCATOR_IODEVICE;
    micLocator.deviceType = SL_IODEVICE_AUDIOINPUT;
    micLocator.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; //micDeviceID;
    micLocator.device = NULL;
    audioSource.pLocator = (void *) &micLocator;
    audioSource.pFormat = NULL;

    /* Setup the data source structure for the buffer queue */
    simpleBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    simpleBufferQueue.numBuffers = N_REC_QUEUE_BUFFERS;
    /* Setup the format of the content in the buffer queue */
    pcm.formatType = SL_DATAFORMAT_PCM;
    pcm.numChannels = 1;
    // _samplingRateIn is initialized in initSampleRate()
    pcm.samplesPerSec = SL_SAMPLINGRATE_16;
    pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    pcm.containerSize = 16;
    pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
    pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    audioSink.pFormat = (void *) &pcm;
    audioSink.pLocator = (void *) &simpleBufferQueue;

    // Create audio recorder 
    const SLInterfaceID id[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
    const SLboolean req[1] = { SL_BOOLEAN_TRUE };
    res = (*_slEngine)->CreateAudioRecorder(_slEngine, &_slRecorder,
                                            &audioSource, &audioSink, 1, id,
                                            req);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to create Recorder");
        return -1;
    }

    // Realizing the recorder in synchronous mode.
    res = (*_slRecorder)->Realize(_slRecorder, SL_BOOLEAN_FALSE);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to realize Recorder");
        return -1;
    }

    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                 "  get Recorder interface");
    // Get the RECORD interface - it is an implicit interface
    res = (*_slRecorder)->GetInterface(_slRecorder, SL_IID_RECORD,
                                       (void*) &_slRecorderRecord);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get Recorder interface");
        return -1;
    }

    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                 "  get Recorder Simple Buffer Queue 2");
    // Get the simpleBufferQueue interface
    res = (*_slRecorder)->GetInterface(_slRecorder,
                                       SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                       (void*) &_slRecorderSimpleBufferQueue);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to get Recorder Simple Buffer Queue");
        return -1;
    }

    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                 "  register Recorder Callback 2");
    // Setup to receive buffer queue event callbacks
    res = (*_slRecorderSimpleBufferQueue)->RegisterCallback(
        _slRecorderSimpleBufferQueue,
        RecorderSimpleBufferQueueCallback,
        this);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to register Recorder Callback");
        return -1;
    }

    _recIsInitialized = true;
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StartRecording() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_recIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recording not initialized");
        return -1;
    }

    if (_recording) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Recording already started");
        return 0;
    }

    if (_slRecorderRecord == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  RecordITF is NULL");
        return -1;
    }

    if (_slRecorderSimpleBufferQueue == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Recorder Simple Buffer Queue is NULL");
        return -1;
    }

    // Reset recording buffer
    memset(_recQueueBuffer, 0, sizeof(_recQueueBuffer)); // empty the queue
    _recQueueSeq = 0;

    const char* threadName = "webrtc_opensles_audio_capture_thread";
    _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this,
            kRealtimePriority, threadName);
    if (_ptrThreadRec == NULL)
    {
        WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
                "  failed to create the rec audio thread");
        return -1;
    }

    unsigned int threadID(0);
    if (!_ptrThreadRec->Start(threadID))
    {
        WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
                "  failed to start the rec audio thread");
        delete _ptrThreadRec;
        _ptrThreadRec = NULL;
        return -1;
    }
    _recThreadID = threadID;
    _recThreadIsInitialized = true;
    memset(_recBuffer, 0, sizeof(_recBuffer));
    memset(_recLength, 0, sizeof(_recLength));
    memset(_recSeqNumber, 0, sizeof(_recSeqNumber));
    _recCurrentSeq = 0;
    _recBufferTotalSize = 0;
    _recWarning = 0;
    _recError = 0;

    // Enqueue N_REC_QUEUE_BUFFERS -1 zero buffers to get the ball rolling
    // find out how it behaves when the sample rate is 44100
    WebRtc_Word32 res(-1);
    WebRtc_UWord32 nSample10ms = _adbSampleRate / 100;
    for (int i = 0; i < (N_REC_QUEUE_BUFFERS - 1); i++) {
        // We assign 10ms buffer to each queue, size given in bytes.
        res = (*_slRecorderSimpleBufferQueue)->Enqueue(
            _slRecorderSimpleBufferQueue,
            (void*) _recQueueBuffer[_recQueueSeq],
            2 * nSample10ms);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to Enqueue Empty Buffer to recorder");
            return -1;
        }
        _recQueueSeq++;
    }
    // Record the audio
    res = (*_slRecorderRecord)->SetRecordState(_slRecorderRecord,
                                               SL_RECORDSTATE_RECORDING);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to start recording");
        return -1;
    }
    _recording = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StopRecording() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_recIsInitialized) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Recording is not initialized");
        return 0;
    }

    // Stop the recording thread
    if (_ptrThreadRec != NULL)
    {
        WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
                "Stopping capture thread");
        bool res = _ptrThreadRec->Stop();
        if (!res) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                                    "Failed to stop Capture thread ");
        } else {
            delete _ptrThreadRec;
            _ptrThreadRec = NULL;
            _recThreadIsInitialized = false;
        }
    }

    if ((_slRecorderRecord != NULL) && (_slRecorder != NULL)) {
        // Record the audio
        WebRtc_Word32 res = (*_slRecorderRecord)->SetRecordState(
            _slRecorderRecord,
            SL_RECORDSTATE_STOPPED);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to stop recording");
            return -1;
        }
        res = (*_slRecorderSimpleBufferQueue)->Clear(
              _slRecorderSimpleBufferQueue);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to clear recorder buffer queue");
            return -1;
        }

        // Destroy the recorder object 
        (*_slRecorder)->Destroy(_slRecorder);
        _slRecorder = NULL;
        _slRecorderRecord = NULL;
        _slRecorderRecord = NULL;
    }

    _recIsInitialized = false;
    _recording = false;
    _recWarning = 0;
    _recError = 0;
    _recQueueSeq = 0;
    return 0;
}

bool AudioDeviceAndroidOpenSLES::RecordingIsInitialized() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _recIsInitialized;
}


bool AudioDeviceAndroidOpenSLES::Recording() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _recording;
}

bool AudioDeviceAndroidOpenSLES::PlayoutIsInitialized() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _playIsInitialized;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StartPlayout() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_playIsInitialized) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  Playout not initialized");
        return -1;
    }

    if (_playing) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Playout already started");
        return 0;
    }

    if (_slPlayerPlay == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  PlayItf is NULL");
        return -1;
    }
    if (_slPlayerSimpleBufferQueue == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  PlayerSimpleBufferQueue is NULL");
        return -1;
    }

    _recQueueSeq = 0;

    WebRtc_Word32 res(-1);
    /* Enqueue a set of zero buffers to get the ball rolling */
    WebRtc_UWord32 nSample10ms = _adbSampleRate / 100;
    WebRtc_Word8 playBuffer[2 * nSample10ms];
    WebRtc_UWord32 noSamplesOut(0);
    /*    res = (*_slPlayerSimpleBufferQueue)->Clear(_slPlayerSimpleBufferQueue);
     if (res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
     "  player simpler buffer queue clean failed");
     //return ; dong return
     }
     *///    for (int i = 0; i<(N_PLAY_QUEUE_BUFFERS -1); i++)
    {
        noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(nSample10ms);
        //Lock();
        // Get data from Audio Device Buffer
        noSamplesOut = _ptrAudioBuffer->GetPlayoutData(playBuffer);
        // Insert what we have in data buffer
        memcpy(_playQueueBuffer[_playQueueSeq], playBuffer, 2 * noSamplesOut);
        //UnLock();

        //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
        // "_playQueueSeq (%u)  noSamplesOut (%d)", _playQueueSeq,
        //noSamplesOut);
        // write the buffer data we got from VoE into the device
        res = (*_slPlayerSimpleBufferQueue)->Enqueue(
            _slPlayerSimpleBufferQueue,
            (void*) _playQueueBuffer[_playQueueSeq],
            2 * noSamplesOut);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                         "  player simpler buffer queue Enqueue failed, %d",
                         noSamplesOut);
            //return ; dong return
        }
        _playQueueSeq = (_playQueueSeq + 1) % N_PLAY_QUEUE_BUFFERS;
    }

    // Play the PCM samples using a buffer queue
    res = (*_slPlayerPlay)->SetPlayState(_slPlayerPlay, SL_PLAYSTATE_PLAYING);
    if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to start playout");
        return -1;
    }

    _playWarning = 0;
    _playError = 0;
    _playing = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StopPlayout() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    CriticalSectionScoped lock(_critSect);

    if (!_playIsInitialized) {
        WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
                     "  Playout is not initialized");
        return 0;
    }

    if ((_slPlayerPlay != NULL) && (_slOutputMixObject == NULL) && (_slPlayer
            == NULL)) {
        // Make sure player is stopped 
        WebRtc_Word32 res =
                (*_slPlayerPlay)->SetPlayState(_slPlayerPlay,
                                               SL_PLAYSTATE_STOPPED);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to stop playout");
            return -1;
        }
        res = (*_slPlayerSimpleBufferQueue)->Clear(_slPlayerSimpleBufferQueue);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                         "  failed to clear recorder buffer queue");
            return -1;
        }

        // Destroy the player
        (*_slPlayer)->Destroy(_slPlayer);
        // Destroy Output Mix object 
        (*_slOutputMixObject)->Destroy(_slOutputMixObject);
        _slPlayer = NULL;
        _slPlayerPlay = NULL;
        _slPlayerSimpleBufferQueue = NULL;
        _slOutputMixObject = NULL;
    }

    _playIsInitialized = false;
    _playing = false;
    _playWarning = 0;
    _playError = 0;
    _playQueueSeq = 0;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutDelay(WebRtc_UWord16& delayMS) const {
    delayMS = _playoutDelay;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::RecordingDelay(WebRtc_UWord16& delayMS) const {
    delayMS = _recordingDelay;

    return 0;
}

bool AudioDeviceAndroidOpenSLES::Playing() const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    return _playing;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetPlayoutBuffer(
    const AudioDeviceModule::BufferType /*type*/,
    WebRtc_UWord16 /*sizeMS*/) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutBuffer(
    AudioDeviceModule::BufferType& type,
    WebRtc_UWord16& sizeMS) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    type = AudioDeviceModule::kAdaptiveBufferSize;
    sizeMS = _playoutDelay; // Set to current playout delay

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::CPULoad(WebRtc_UWord16& /*load*/) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

bool AudioDeviceAndroidOpenSLES::PlayoutWarning() const {
    return (_playWarning > 0);
}

bool AudioDeviceAndroidOpenSLES::PlayoutError() const {
    return (_playError > 0);
}

bool AudioDeviceAndroidOpenSLES::RecordingWarning() const {
    return (_recWarning > 0);
}

bool AudioDeviceAndroidOpenSLES::RecordingError() const {
    return (_recError > 0);
}

void AudioDeviceAndroidOpenSLES::ClearPlayoutWarning() {
    _playWarning = 0;
}

void AudioDeviceAndroidOpenSLES::ClearPlayoutError() {
    _playError = 0;
}

void AudioDeviceAndroidOpenSLES::ClearRecordingWarning() {
    _recWarning = 0;
}

void AudioDeviceAndroidOpenSLES::ClearRecordingError() {
    _recError = 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetLoudspeakerStatus(bool enable) {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s(%d)",
                 __FUNCTION__, enable);

    //  if (!_javaContext)
    //  {
    //        WEBRTC_TRACE(kTraceError, kTraceUtility, -1, "  Context is not set");
    //        return -1;
    //  }

    //  get the JNI env for this thread
    //   JNIEnv *env;
    //   bool isAttached = false;

    //  if (_javaVM->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
    //  {
    // try to attach the thread and get the env
    // Attach this thread to JVMslPlayoutCallback
    //       jint res = _javaVM->AttachCurrentThread(&env, NULL);

    // Get the JNI env for this thread
    //       if ((res < 0) || !env)
    //      {
    //           WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
    // "  Could not attach thread to JVM (%d, %p)", res, env);
    //           return -1;
    //       }
    //       isAttached = true;
    //   }

    // get the method ID
    //   jmethodID setPlayoutSpeakerID = env->GetMethodID(_javaScClass,
    // "SetPlayoutSpeaker", "(Z)I");

    // call java sc object method
    //    jint res = env->CallIntMethod(_javaScObj, setPlayoutSpeakerID, enable);
    //   if (res < 0)
    //  {
    //    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
    // "  SetPlayoutSpeaker failed (%d)", res);
    //      return -1;
    //  }

    _loudSpeakerOn = enable;

    //  Detach this thread if it was attached
    //    if (isAttached)
    //    {
    //        if (_javaVM->DetachCurrentThread() < 0)
    //       {
    //            WEBRTC_TRACE(kTraceWarning, kTraceUtility, -1,
    // "  Could not detach thread from JVM");
    //       }
    //    }

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::GetLoudspeakerStatus(
    bool& enabled) const {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    enabled = _loudSpeakerOn;
    return 0;
}

// ============================================================================
//                                 Private Methods
// ============================================================================

void AudioDeviceAndroidOpenSLES::PlayerSimpleBufferQueueCallback(
    SLAndroidSimpleBufferQueueItf queueItf,
    void *pContext) {
    AudioDeviceAndroidOpenSLES* ptrThis =
            static_cast<AudioDeviceAndroidOpenSLES*> (pContext);
    ptrThis->PlayerSimpleBufferQueueCallbackHandler(queueItf);
}

void AudioDeviceAndroidOpenSLES::PlayerSimpleBufferQueueCallbackHandler(
    SLAndroidSimpleBufferQueueItf queueItf) {
    WebRtc_Word32 res;
    //Lock();
    //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    //"_playQueueSeq (%u)", _playQueueSeq);
    if (_playing && (_playQueueSeq < N_PLAY_QUEUE_BUFFERS)) {
        //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice,
        //_id, "playout callback ");
        unsigned int noSamp10ms = _adbSampleRate / 100;
        // Max 10 ms @ samplerate kHz / 16 bit
        WebRtc_Word8 playBuffer[2 * noSamp10ms];
        int noSamplesOut = 0;

        // Assumption for implementation
        // assert(PLAYBUFSIZESAMPLES == noSamp10ms);

        // TODO(xians), update the playout delay
        //UnLock();

        noSamplesOut = _ptrAudioBuffer->RequestPlayoutData(noSamp10ms);
        //Lock();
        // Get data from Audio Device Buffer
        noSamplesOut = _ptrAudioBuffer->GetPlayoutData(playBuffer);
        // Cast OK since only equality comparison
        if (noSamp10ms != (unsigned int) noSamplesOut) {
            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                         "noSamp10ms (%u) != noSamplesOut (%d)", noSamp10ms,
                         noSamplesOut);

            if (_playWarning > 0) {
                WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                             "  Pending play warning exists");
            }
            _playWarning = 1;
        }
        // Insert what we have in data buffer
        memcpy(_playQueueBuffer[_playQueueSeq], playBuffer, 2 * noSamplesOut);
        //UnLock();

        //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
        //"_playQueueSeq (%u)  noSamplesOut (%d)", _playQueueSeq, noSamplesOut);
        // write the buffer data we got from VoE into the device
        res
                = (*_slPlayerSimpleBufferQueue)->Enqueue(
                                                         _slPlayerSimpleBufferQueue,
                                                         _playQueueBuffer[_playQueueSeq],
                                                         2 * noSamplesOut);
        if (res != SL_RESULT_SUCCESS) {
            WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                         "  player simpler buffer queue Enqueue failed, %d",
                         noSamplesOut);
            return;
        }
        // update the playout delay
        UpdatePlayoutDelay(noSamplesOut);
        // update the play buffer sequency
        _playQueueSeq = (_playQueueSeq + 1) % N_PLAY_QUEUE_BUFFERS;
    }
}

void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallback(
    SLAndroidSimpleBufferQueueItf queueItf,
    void *pContext) {
    AudioDeviceAndroidOpenSLES* ptrThis =
            static_cast<AudioDeviceAndroidOpenSLES*> (pContext);
    ptrThis->RecorderSimpleBufferQueueCallbackHandler(queueItf);
}

void AudioDeviceAndroidOpenSLES::RecorderSimpleBufferQueueCallbackHandler(
    SLAndroidSimpleBufferQueueItf queueItf) {
    WebRtc_Word32 res;
    //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    //"  RecorderSimpleBufferQueueCallbackHandler");
    if (_recording) {
        // Insert all data in temp buffer into recording buffers
        // There is zero or one buffer partially full at any given time,
        // all others are full or empty
        // Full means filled with noSamp10ms samples.

        const unsigned int noSamp10ms = _adbSampleRate / 100;
        //        WebRtc_UWord16 queuePos = 0;
        //        WebRtc_UWord16 checkQueuePos = 0;
        unsigned int dataPos = 0;
        WebRtc_UWord16 bufPos = 0;
        WebRtc_Word16 insertPos = -1;
        unsigned int nCopy = 0; // Number of samples to copy
        //        WebRtc_Word32 isData = 0;

        while (dataPos < noSamp10ms)//REC_BUF_SIZE_IN_SAMPLES) //noSamp10ms)

        {
            // Loop over all recording buffers or until we find the partially
            // full buffer
            // First choice is to insert into partially full buffer,
            // second choice is to insert into empty buffer
            bufPos = 0;
            insertPos = -1;
            nCopy = 0;
            while (bufPos < N_REC_BUFFERS)
            {
                if ((_recLength[bufPos] > 0) && (_recLength[bufPos]
                                < noSamp10ms))
                {
                    // Found the partially full buffer
                    insertPos = static_cast<WebRtc_Word16> (bufPos);
                    bufPos = N_REC_BUFFERS; // Don't need to search more
                }
                else if ((-1 == insertPos) && (0 == _recLength[bufPos]))
                {
                    // Found an empty buffer
                    insertPos = static_cast<WebRtc_Word16> (bufPos);
                }
                ++bufPos;
            }

            if (insertPos > -1)
            {
                // We found a non-full buffer, copy data from the buffer queue
                // o recBuffer
                unsigned int dataToCopy = noSamp10ms - dataPos;
                unsigned int currentRecLen = _recLength[insertPos];
                unsigned int roomInBuffer = noSamp10ms - currentRecLen;
                nCopy = (dataToCopy < roomInBuffer ? dataToCopy : roomInBuffer);
                memcpy(&_recBuffer[insertPos][currentRecLen],
                        &_recQueueBuffer[_recQueueSeq][dataPos],
                        nCopy * sizeof(short));
                if (0 == currentRecLen)
                {
                    _recSeqNumber[insertPos] = _recCurrentSeq;
                    ++_recCurrentSeq;
                }
                _recBufferTotalSize += nCopy;
                // Has to be done last to avoid interrupt problems
                // between threads
                _recLength[insertPos] += nCopy;
                dataPos += nCopy;
            }
            else
            {
                // Didn't find a non-full buffer
                WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice,
                        _id, "  Could not insert into recording buffer");
                if (_recWarning > 0)
                {
                    WEBRTC_TRACE(kTraceWarning,
                            kTraceAudioDevice, _id,
                            "  Pending rec warning exists");
                }
                _recWarning = 1;
                dataPos = noSamp10ms; // Don't try to insert more
            }
        }

        // clean the queue buffer
        // Start with empty buffer
        memset(_recQueueBuffer[_recQueueSeq], 0, 2 * REC_BUF_SIZE_IN_SAMPLES);
        // write the empty buffer to the queue
        res = (*_slRecorderSimpleBufferQueue)->Enqueue(
              _slRecorderSimpleBufferQueue,
              (void*) _recQueueBuffer[_recQueueSeq],
              2 * noSamp10ms);
        if (res != SL_RESULT_SUCCESS) {
            return;
        }
        // update the rec queue seq
        _recQueueSeq = (_recQueueSeq + 1) % N_REC_QUEUE_BUFFERS;
        // wake up the recording thread
        _timeEventRec.Set();
    }
}

void AudioDeviceAndroidOpenSLES::CheckErr(SLresult res) {
    if (res != SL_RESULT_SUCCESS) {
        // Debug printing to be placed here
        exit(-1);
    }
}

void AudioDeviceAndroidOpenSLES::UpdatePlayoutDelay(WebRtc_UWord32 nSamplePlayed) {
    // currently just do some simple calculation, should we setup a timer for
    // the callback to have a more accurate delay
    // Android CCD asks for 10ms as the maximum warm output latency, so we
    // simply add (nPlayQueueBuffer -1 + 0.5)*10ms
    // This playout delay should be seldom changed
    _playoutDelay = (N_PLAY_QUEUE_BUFFERS - 0.5) * 10 + N_PLAY_QUEUE_BUFFERS
            * nSamplePlayed / (_adbSampleRate / 1000);
}

void AudioDeviceAndroidOpenSLES::UpdateRecordingDelay() {
    // // Android CCD asks for 10ms as the maximum warm input latency,
    // so we simply add 10ms
    _recordingDelay = 10;
    const WebRtc_UWord32 noSamp10ms = _adbSampleRate / 100;
    //    if (_recBufferTotalSize > noSamp10ms)
    //    {
    _recordingDelay += (N_REC_QUEUE_BUFFERS * noSamp10ms) / (_adbSampleRate
            / 1000);
    //    }
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitSampleRate() {
    WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);

    WebRtc_Word32 res(-1);
    WebRtc_Word32 numOutputs(0);
    WebRtc_UWord32 headsetDeviceID(0);
    WebRtc_UWord32 earpieceDeviceID(0);
    bool headsetAvailable(false);
    bool earpieceAvailable(false);
    bool foundSampleRate(false);
    WebRtc_UWord32 outputDeviceIDs[N_MAX_OUTPUT_DEVICES];
    SLAudioOutputDescriptor audioOutputDescriptor;

    if (_slEngineObject == NULL) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  SL Object is NULL");
        return -1;
    }

    /*    // Get the Audio IO DEVICE CAPABILITIES interface
     res = (*_slEngineObject)->GetInterface(_slEngineObject,
     SL_IID_AUDIOIODEVICECAPABILITIES, (void*)&_slAudioIODeviceCapabilities);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get Device Capabilities interface");
     return -1;
     }
     numOutputs = N_MAX_OUTPUT_DEVICES;
     res = (*_slAudioIODeviceCapabilities)->GetAvailableAudioOutputs(
     _slAudioIODeviceCapabilities, &numOutputs, outputDeviceIDs);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to get number of Output Devices");
     return -1;
     }

     // Search for headset output and phone handsfree speaker device,
     // we prefer headset to earpiece
     for (int i=0;i<numOutputs; i++)
     {
     res = (*_slAudioIODeviceCapabilities)->QueryAudioOutputCapabilities(
     _slAudioIODeviceCapabilities, outputDeviceIDs[i], &audioOutputDescriptor);
     if ( res != SL_RESULT_SUCCESS)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  failed to query Output Devices info");
     return -1;
     }
     if((audioOutputDescriptor.deviceConnection ==
     SL_DEVCONNECTION_ATTACHED_WIRED)&&
     (audioOutputDescriptor.deviceScope == SL_DEVSCOPE_USER)&&
     (audioOutputDescriptor.deviceLocation == SL_DEVLOCATION_HEADSET))
     {
     headsetDeviceID = outputDeviceIDs[i];
     headsetAvailable = true;
     break;
     }
     else if((audioOutputDescriptor.deviceConnection ==
     SL_DEVCONNECTION_INTEGRATED)&&
     (audioOutputDescriptor.deviceScope == SL_DEVSCOPE_ENVIRONMENT)&&
     (audioOutputDescriptor.deviceLocation == SL_DEVLOCATION_HANDSET))
     {
     earpieceDeviceID = outputDeviceIDs[i];
     earpieceAvailable = true;
     break;
     }
     }
     // Neither headset output nor phone handsfree speaker is available
     if (headsetAvailable == false && earpieceAvailable == false)
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  No playout device");
     return -1;
     }

     _sampleRateInMilliHz = SL_SAMPLINGRATE_16;
     if (audioOutputDescriptor.isFreqRangeContinuous == SL_BOOLEAN_FALSE)
     {
     while (!foundSampleRate)
     {
     for (int i=0; i<audioOutputDescriptor.numOfSamplingRatesSupported; i++)
     {
     if (audioOutputDescriptor.samplingRatesSupported[i]
     == _sampleRateInMilliHz) // supported sampling rate in milliHertz
     {
     switch (_sampleRateInMilliHz)
     {
     case SL_SAMPLINGRATE_44_1:
     _adbSampleRate = 44000;
     break;
     case SL_SAMPLINGRATE_16:
     _adbSampleRate = 16000;
     break;
     case SL_SAMPLINGRATE_8:
     _adbSampleRate = 8000;
     break;
     default: // error
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  SampleRate(%d) is not supported", _sampleRateInMilliHz);
     return -1;
     } // switch
     foundSampleRate = true;
     break;
     } //if(audioOutputDescriptor.samplingRatesSupported[i] == _sampleRate
     } //for (int i=0; i<audioOutputDescriptor.numOfSamplingRatesSupported; i++)
     switch (_sampleRateInMilliHz)
     {
     case SL_SAMPLINGRATE_16:
     _sampleRateInMilliHz = SL_SAMPLINGRATE_44_1;
     break;
     case SL_SAMPLINGRATE_44_1:
     _sampleRateInMilliHz = SL_SAMPLINGRATE_8;
     break;
     default:
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "  SampleRate is not supported");
     return -1;
     } // switch
     } // while (!foundSampleRate)
     }
     else //audioOutputDescriptor.isFreqRangeContinuous == SL_BOOLEAN_TRUE
     {
     // minSampleRate < SL_SAMPLINGRATE_16 < maxSampleRate
     if ((audioOutputDescriptor.minSampleRate < SL_SAMPLINGRATE_16) &&
     (SL_SAMPLINGRATE_16 < audioOutputDescriptor.maxSampleRate))
     {
     _adbSampleRate = 16000;
     } // minSampleRate < SL_SAMPLINGRATE_44_1 < maxSampleRate
     else if((audioOutputDescriptor.minSampleRate < SL_SAMPLINGRATE_44_1) &&
     (SL_SAMPLINGRATE_44_1 < audioOutputDescriptor.maxSampleRate))
     {
     _adbSampleRate = 44000;
     } // minSampleRate < SL_SAMPLINGRATE_8 < maxSampleRate
     else if ((audioOutputDescriptor.minSampleRate < SL_SAMPLINGRATE_8) &&
     (SL_SAMPLINGRATE_8 < audioOutputDescriptor.maxSampleRate))
     {
     _adbSampleRate = 8000;
     }
     else
     {
     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
     "  SampleRate is not supported");
     return -1;
     }
     } // else
     */
    _samplingRateIn = SL_SAMPLINGRATE_16;
    _samplingRateOut = SL_SAMPLINGRATE_16;
    _adbSampleRate = 16000;

    WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id,
                 "  sample rate set to (%d)", _adbSampleRate);
    return 0;

}

// ============================================================================
//                                  Thread Methods
// ============================================================================

bool AudioDeviceAndroidOpenSLES::RecThreadFunc(void* pThis) {
    return (static_cast<AudioDeviceAndroidOpenSLES*> (pThis)->RecThreadProcess());
}

bool AudioDeviceAndroidOpenSLES::RecThreadProcess() {

    //    Lock();
    // Wait for 100ms for the signal from device callback
    // In case no callback comes in 100ms, we check the buffer anyway
    _timeEventRec.Wait(100);

    int bufPos = 0;
    unsigned int lowestSeq = 0;
    int lowestSeqBufPos = 0;
    bool foundBuf = true;
    const unsigned int noSamp10ms = _adbSampleRate / 100;

    while (foundBuf)
    {
        // Check if we have any buffer with data to insert into the
        // Audio Device Buffer,
        // and find the one with the lowest seq number
        foundBuf = false;

        for (bufPos = 0; bufPos < N_REC_BUFFERS; ++bufPos)
        {
            if (noSamp10ms == _recLength[bufPos])
            {
                if (!foundBuf) {
                    lowestSeq = _recSeqNumber[bufPos];
                    lowestSeqBufPos = bufPos;
                    foundBuf = true;
                } else if (_recSeqNumber[bufPos] < lowestSeq)
                {
                    lowestSeq = _recSeqNumber[bufPos];
                    lowestSeqBufPos = bufPos;
                }
            }
        } // for

        // Insert data into the Audio Device Buffer if found any
        if (foundBuf)
        {
            UpdateRecordingDelay();
            // Set the recorded buffer
            _ptrAudioBuffer->SetRecordedBuffer(_recBuffer[lowestSeqBufPos],
                                               noSamp10ms);

            // Don't need to set the current mic level in ADB since we only
            // support digital AGC,
            // and besides we cannot get or set the iPhone mic level anyway.

            // Set VQE info, use clockdrift == 0
            _ptrAudioBuffer->SetVQEData(_playoutDelay, _recordingDelay, 0);

            // Deliver recorded samples at specified sample rate, mic level
            // etc. to the observer using callback
            //UnLock();
            _ptrAudioBuffer->DeliverRecordedData();
            //Lock();

            // Make buffer available
            _recSeqNumber[lowestSeqBufPos] = 0;
            _recBufferTotalSize -= _recLength[lowestSeqBufPos];
            // Must be done last to avoid interrupt problems between threads
            _recLength[lowestSeqBufPos] = 0;
        }

    } // while (foundBuf)
    //UnLock();
    return true;
}

} // namespace webrtc
