/*
 *  Copyright (c) 2012 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 <sys/resource.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>

#include "audio_device_utility.h"
#include "audio_device_opensles_android.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),
    _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),
    is_thread_priority_set_(false) {
    WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created",
                 __FUNCTION__);
    memset(_playQueueBuffer, 0, sizeof(_playQueueBuffer));
}

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

    Terminate();

    delete &_timeEventRec;
    delete &_critSect;
}

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

void AudioDeviceAndroidOpenSLES::AttachAudioBuffer(
    AudioDeviceBuffer* audioBuffer) {

    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 {

    audioLayer = AudioDeviceModule::kPlatformDefaultAudio;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::Init() {

    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() {

    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 {

    return (_initialized);
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerIsAvailable(bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitSpeaker() {

    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) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::InitMicrophone() {

    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 {

    return _speakerIsInitialized;
}

bool AudioDeviceAndroidOpenSLES::MicrophoneIsInitialized() const {

    return _micIsInitialized;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolumeIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetSpeakerVolume(
    WebRtc_UWord32 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;
        }
    }
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolume(
    WebRtc_UWord32& volume) const {
    return 0;
}

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetWaveOutVolume(
    WebRtc_UWord16 /*volumeLeft*/,
    WebRtc_UWord16 /*volumeRight*/) {

    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(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MaxSpeakerVolume(
    WebRtc_UWord32& maxVolume) const {

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

    maxVolume = _maxSpeakerVolume;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MinSpeakerVolume(
    WebRtc_UWord32& minVolume) const {

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

    minVolume = _minSpeakerVolume;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerVolumeStepSize(
    WebRtc_UWord16& stepSize) const {

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

    return 0;
}

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerMuteIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetSpeakerMute(bool /*enable*/) {

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SpeakerMute(bool& /*enabled*/) const {

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneMuteIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneMute(bool /*enable*/) {

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneMute(
    bool& /*enabled*/) const {

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

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneBoostIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneBoost(bool enable) {

    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 {

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

    enabled = false;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoRecordingIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetStereoRecording(bool enable) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoRecording(bool& enabled) const {

    enabled = false;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoPlayoutIsAvailable(
    bool& available) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetStereoPlayout(bool enable) {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StereoPlayout(bool& enabled) const {

    enabled = false;

    return 0;
}


WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetAGC(bool enable) {

    _AGC = enable;

    return 0;
}

bool AudioDeviceAndroidOpenSLES::AGC() const {

    return _AGC;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneVolumeIsAvailable(
    bool& available) {

    available = true;

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetMicrophoneVolume(
    WebRtc_UWord32 volume) {

    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 {
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MaxMicrophoneVolume(
    WebRtc_UWord32& /*maxVolume*/) const {
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MinMicrophoneVolume(
    WebRtc_UWord32& minVolume) const {

    minVolume = 0;
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::MicrophoneVolumeStepSize(
    WebRtc_UWord16& stepSize) const {

    stepSize = 1;
    return 0;
}

WebRtc_Word16 AudioDeviceAndroidOpenSLES::PlayoutDevices() {

    return 1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetPlayoutDevice(
    WebRtc_UWord16 index) {

    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(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutDeviceName(
    WebRtc_UWord16 index,
    char name[kAdmMaxDeviceNameSize],
    char guid[kAdmMaxGuidSize]) {

    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,
    char name[kAdmMaxDeviceNameSize],
    char guid[kAdmMaxGuidSize]) {

    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() {

    return 1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetRecordingDevice(
    WebRtc_UWord16 index) {

    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(kTraceWarning, kTraceAudioDevice, _id,
                 "  API call not supported on this platform");
    return -1;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::PlayoutIsAvailable(bool& available) {

    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) {

    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() {

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

    // 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() {

    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);
    SLDataSource audioSource;
    SLDataLocator_IODevice micLocator;
    SLDataSink audioSink;
    SLDataFormat_PCM pcm;
    SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue;

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

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

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

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

    // 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() {

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

    // Make sure the queues are empty.
    assert(rec_callback_queue_.empty());
    assert(rec_available_queue_.empty());
    assert(rec_worker_queue_.empty());

    // Reset recording buffer and put them to the available buffer queue.
    memset(rec_buffer_, 0, sizeof(rec_buffer_)); // empty the queue
    for (int i = 0; i < N_REC_BUFFERS; ++i) {
      rec_available_queue_.push(rec_buffer_[i]);
    }

    const char* threadName = "sles_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;
    _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) {
      int8_t* buf = rec_available_queue_.front();
      rec_available_queue_.pop();
      rec_callback_queue_.push(buf);
      // We assign 10ms buffer to each queue, size given in bytes.
      res = (*_slRecorderSimpleBufferQueue)->Enqueue(
          _slRecorderSimpleBufferQueue,
          buf,
          2 * nSample10ms);
      if (res != SL_RESULT_SUCCESS) {
        WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
                     "  failed to Enqueue Empty Buffer to recorder");
        return -1;
      }
    }

    // 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() {
  {
    CriticalSectionScoped lock(&_critSect);

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

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

  CriticalSectionScoped lock(&_critSect);
  if ((_slRecorderRecord != NULL) && (_slRecorder != NULL)) {
    // Record the audio
    int 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;
  is_thread_priority_set_ = false;

  // Clear the callback queue.
  while(!rec_callback_queue_.empty())
    rec_callback_queue_.pop();

  // Clear the available buffer queue.
  while(!rec_available_queue_.empty())
    rec_available_queue_.pop();

  // Clear the buffer queue.
  while(!rec_worker_queue_.empty())
    rec_worker_queue_.pop();

  return 0;
}

bool AudioDeviceAndroidOpenSLES::RecordingIsInitialized() const {

    return _recIsInitialized;
}


bool AudioDeviceAndroidOpenSLES::Recording() const {

    return _recording;
}

bool AudioDeviceAndroidOpenSLES::PlayoutIsInitialized() const {

    return _playIsInitialized;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::StartPlayout() {

    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);
    {
        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() {

    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 {

    return _playing;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::SetPlayoutBuffer(
    const AudioDeviceModule::BufferType /*type*/,
    WebRtc_UWord16 /*sizeMS*/) {

    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 {

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

    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::CPULoad(WebRtc_UWord16& /*load*/) const {

    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) {
    _loudSpeakerOn = enable;
    return 0;
}

WebRtc_Word32 AudioDeviceAndroidOpenSLES::GetLoudspeakerStatus(
    bool& enabled) const {

    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) {
  if (_recording) {
    const unsigned int samples_10_ms = _adbSampleRate / 100;

    // Move the buffer from the callback queue to buffer queue so that VoE can
    // process the data in RecThreadProcess().
    int8_t* buf = rec_callback_queue_.front();
    rec_callback_queue_.pop();
    int8_t* new_buf = NULL;
    {
      // |rec_available_queue_| and |rec_worker_queue_| are accessed by
      // callback thread and recording thread, so we need a lock here to
      // protect them.
      CriticalSectionScoped lock(&_critSect);
      if (!rec_available_queue_.empty()) {
        // Put the data to buffer queue for VoE to process the data.
        rec_worker_queue_.push(buf);
        new_buf = rec_available_queue_.front();
        rec_available_queue_.pop();
        // TODO(xians): Remove the following test code once we are sure it
        // won't happen anymore.
        if (rec_worker_queue_.size() > 10) {
          WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
                       "Number of buffers pending in the recording thread"
                       " has been increased to %d", rec_worker_queue_.size());
        }
      } else {
        // Didn't find an empty buffer, probably VoE is slowed on processing
        // the data. Put the buffer back to the callback queue so that we can
        // keep the recording rolling. But this means we are losing 10ms data.
        // TODO(xians): Enlarge the buffer instead of dropping data?
        new_buf = buf;

        WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                     "No available buffer slot in |rec_available_queue_|"
                     " It will lose 10ms data");
        _recWarning = 1;
      }
    }

    // Clear the new buffer and enqueue for new data.
    memset(new_buf, 0, 2 * REC_BUF_SIZE_IN_SAMPLES);
    rec_callback_queue_.push(new_buf);
    if (SL_RESULT_SUCCESS != (*_slRecorderSimpleBufferQueue)->Enqueue(
        _slRecorderSimpleBufferQueue,
        static_cast<void*>(new_buf),
        2 * samples_10_ms)) {
      WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
                   "Failed on Enqueue()");
      _recWarning = 1;
    }

    // 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
    int max_warm_input_latency = 10;
    int samples_per_queue_in_ms = 10;
    _recordingDelay = max_warm_input_latency + ((rec_worker_queue_.size() +
        N_REC_QUEUE_BUFFERS) * samples_per_queue_in_ms);
}

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

    _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() {
  if (!is_thread_priority_set_) {
    // TODO(xians): Move the thread setting code to thread_posix.cc. Figure out
    // if we should raise the priority to THREAD_PRIORITY_URGENT_AUDIO(-19).
    int nice_value = -16; // THREAD_PRIORITY_AUDIO in Android.
    if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), nice_value)) {
      WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
                   "Failed to set nice value of thread to %d ", nice_value);
    }

    is_thread_priority_set_ = true;
  }

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

  const unsigned int noSamp10ms = _adbSampleRate / 100;
  bool buffer_available = true;
  while (buffer_available) {
    {
      CriticalSectionScoped lock(&_critSect);
      if (rec_worker_queue_.empty())
        break;

      // Release the buffer from the |rec_worker_queue_| and pass the data to
      // VoE.
      int8_t* buf = rec_worker_queue_.front();
      rec_worker_queue_.pop();
      buffer_available = !rec_worker_queue_.empty();
      // Set the recorded buffer.
      _ptrAudioBuffer->SetRecordedBuffer(buf, noSamp10ms);

      // Put the free buffer to |rec_available_queue_|.
      rec_available_queue_.push(buf);

      // Update the recording delay.
      UpdateRecordingDelay();
    }

    // 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.
    _ptrAudioBuffer->DeliverRecordedData();
  }

  return true;
}

} // namespace webrtc
