C++程序  |  2233行  |  67.83 KB

/*
** Copyright 2010, The Android Open-Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <math.h>

//#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardware"

#include <utils/Log.h>
#include <utils/String8.h>

#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <dlfcn.h>
#include <fcntl.h>

#include "AudioHardware.h"
#include <audio_effects/effect_aec.h>

extern "C" {
#include <tinyalsa/asoundlib.h>
}


namespace android_audio_legacy {

const uint32_t AudioHardware::inputConfigTable[][AudioHardware::INPUT_CONFIG_CNT] = {
        {8000, 4},
        {11025, 4},
        {16000, 2},
        {22050, 2},
        {32000, 1},
        {44100, 1}
};

//  trace driver operations for dump
//
#define DRIVER_TRACE

enum {
    DRV_NONE,
    DRV_PCM_OPEN,
    DRV_PCM_CLOSE,
    DRV_PCM_WRITE,
    DRV_PCM_READ,
    DRV_MIXER_OPEN,
    DRV_MIXER_CLOSE,
    DRV_MIXER_GET,
    DRV_MIXER_SEL
};

#ifdef DRIVER_TRACE
#define TRACE_DRIVER_IN(op) mDriverOp = op;
#define TRACE_DRIVER_OUT mDriverOp = DRV_NONE;
#else
#define TRACE_DRIVER_IN(op)
#define TRACE_DRIVER_OUT
#endif

// ----------------------------------------------------------------------------

const char *AudioHardware::inputPathNameDefault = "Default";
const char *AudioHardware::inputPathNameCamcorder = "Camcorder";
const char *AudioHardware::inputPathNameVoiceRecognition = "Voice Recognition";

AudioHardware::AudioHardware() :
    mInit(false),
    mMicMute(false),
    mPcm(NULL),
    mMixer(NULL),
    mPcmOpenCnt(0),
    mMixerOpenCnt(0),
    mInCallAudioMode(false),
    mVoiceVol(1.0f),
    mInputSource(AUDIO_SOURCE_DEFAULT),
    mBluetoothNrec(true),
    mTTYMode(TTY_MODE_OFF),
    mSecRilLibHandle(NULL),
    mRilClient(0),
    mActivatedCP(false),
    mEchoReference(NULL),
    mDriverOp(DRV_NONE)
{
    loadRILD();
    mInit = true;
}

AudioHardware::~AudioHardware()
{
    for (size_t index = 0; index < mInputs.size(); index++) {
        closeInputStream(mInputs[index].get());
    }
    mInputs.clear();
    closeOutputStream((AudioStreamOut*)mOutput.get());

    if (mMixer) {
        TRACE_DRIVER_IN(DRV_MIXER_CLOSE)
        mixer_close(mMixer);
        TRACE_DRIVER_OUT
    }
    if (mPcm) {
        TRACE_DRIVER_IN(DRV_PCM_CLOSE)
        pcm_close(mPcm);
        TRACE_DRIVER_OUT
    }

    if (mSecRilLibHandle) {
        if (disconnectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS)
            ALOGE("Disconnect_RILD() error");

        if (closeClientRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS)
            ALOGE("CloseClient_RILD() error");

        mRilClient = 0;

        dlclose(mSecRilLibHandle);
        mSecRilLibHandle = NULL;
    }

    mInit = false;
}

status_t AudioHardware::initCheck()
{
    return mInit ? NO_ERROR : NO_INIT;
}

void AudioHardware::loadRILD(void)
{
    mSecRilLibHandle = dlopen("libsecril-client.so", RTLD_NOW);

    if (mSecRilLibHandle) {
        ALOGV("libsecril-client.so is loaded");

        openClientRILD   = (HRilClient (*)(void))
                              dlsym(mSecRilLibHandle, "OpenClient_RILD");
        disconnectRILD   = (int (*)(HRilClient))
                              dlsym(mSecRilLibHandle, "Disconnect_RILD");
        closeClientRILD  = (int (*)(HRilClient))
                              dlsym(mSecRilLibHandle, "CloseClient_RILD");
        isConnectedRILD  = (int (*)(HRilClient))
                              dlsym(mSecRilLibHandle, "isConnected_RILD");
        connectRILD      = (int (*)(HRilClient))
                              dlsym(mSecRilLibHandle, "Connect_RILD");
        setCallVolume    = (int (*)(HRilClient, SoundType, int))
                              dlsym(mSecRilLibHandle, "SetCallVolume");
        setCallAudioPath = (int (*)(HRilClient, AudioPath))
                              dlsym(mSecRilLibHandle, "SetCallAudioPath");
        setCallClockSync = (int (*)(HRilClient, SoundClockCondition))
                              dlsym(mSecRilLibHandle, "SetCallClockSync");

        if (!openClientRILD  || !disconnectRILD   || !closeClientRILD ||
            !isConnectedRILD || !connectRILD      ||
            !setCallVolume   || !setCallAudioPath || !setCallClockSync) {
            ALOGE("Can't load all functions from libsecril-client.so");

            dlclose(mSecRilLibHandle);
            mSecRilLibHandle = NULL;
        } else {
            mRilClient = openClientRILD();
            if (!mRilClient) {
                ALOGE("OpenClient_RILD() error");

                dlclose(mSecRilLibHandle);
                mSecRilLibHandle = NULL;
            }
        }
    } else {
        ALOGE("Can't load libsecril-client.so");
    }
}

status_t AudioHardware::connectRILDIfRequired(void)
{
    if (!mSecRilLibHandle) {
        ALOGE("connectIfRequired() lib is not loaded");
        return INVALID_OPERATION;
    }

    if (isConnectedRILD(mRilClient)) {
        return OK;
    }

    if (connectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) {
        ALOGE("Connect_RILD() error");
        return INVALID_OPERATION;
    }

    return OK;
}

AudioStreamOut* AudioHardware::openOutputStream(
    uint32_t devices, int *format, uint32_t *channels,
    uint32_t *sampleRate, status_t *status)
{
    sp <AudioStreamOutALSA> out;
    status_t rc;

    { // scope for the lock
        Mutex::Autolock lock(mLock);

        // only one output stream allowed
        if (mOutput != 0) {
            if (status) {
                *status = INVALID_OPERATION;
            }
            return NULL;
        }

        out = new AudioStreamOutALSA();

        rc = out->set(this, devices, format, channels, sampleRate);
        if (rc == NO_ERROR) {
            mOutput = out;
        }
    }

    if (rc != NO_ERROR) {
        if (out != 0) {
            out.clear();
        }
    }
    if (status) {
        *status = rc;
    }

    return out.get();
}

void AudioHardware::closeOutputStream(AudioStreamOut* out) {
    sp <AudioStreamOutALSA> spOut;
    sp<AudioStreamInALSA> spIn;
    {
        Mutex::Autolock lock(mLock);
        if (mOutput == 0 || mOutput.get() != out) {
            ALOGW("Attempt to close invalid output stream");
            return;
        }
        spOut = mOutput;
        mOutput.clear();
        if (mEchoReference != NULL) {
            spIn = getActiveInput_l();
        }
    }
    if (spIn != 0) {
        // this will safely release the echo reference by calling releaseEchoReference()
        // after placing the active input in standby
        spIn->standby();
    }

    spOut.clear();
}

AudioStreamIn* AudioHardware::openInputStream(
    uint32_t devices, int *format, uint32_t *channels,
    uint32_t *sampleRate, status_t *status,
    AudioSystem::audio_in_acoustics acoustic_flags)
{
    // check for valid input source
    if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
        if (status) {
            *status = BAD_VALUE;
        }
        return NULL;
    }

    status_t rc = NO_ERROR;
    sp <AudioStreamInALSA> in;

    { // scope for the lock
        Mutex::Autolock lock(mLock);

        in = new AudioStreamInALSA();
        rc = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
        if (rc == NO_ERROR) {
            mInputs.add(in);
        }
    }

    if (rc != NO_ERROR) {
        if (in != 0) {
            in.clear();
        }
    }
    if (status) {
        *status = rc;
    }

    ALOGV("AudioHardware::openInputStream()%p", in.get());
    return in.get();
}

void AudioHardware::closeInputStream(AudioStreamIn* in) {

    sp<AudioStreamInALSA> spIn;
    {
        Mutex::Autolock lock(mLock);

        ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in);
        if (index < 0) {
            ALOGW("Attempt to close invalid input stream");
            return;
        }
        spIn = mInputs[index];
        mInputs.removeAt(index);
    }
    ALOGV("AudioHardware::closeInputStream()%p", in);
    spIn.clear();
}


status_t AudioHardware::setMode(int mode)
{
    sp<AudioStreamOutALSA> spOut;
    sp<AudioStreamInALSA> spIn;
    status_t status;

    // Mutex acquisition order is always out -> in -> hw
    AutoMutex lock(mLock);

    spOut = mOutput;
    while (spOut != 0) {
        if (!spOut->checkStandby()) {
            int cnt = spOut->prepareLock();
            mLock.unlock();
            spOut->lock();
            mLock.lock();
            // make sure that another thread did not change output state while the
            // mutex is released
            if ((spOut == mOutput) && (cnt == spOut->standbyCnt())) {
                break;
            }
            spOut->unlock();
            spOut = mOutput;
        } else {
            spOut.clear();
        }
    }
    // spOut is not 0 here only if the output is active

    spIn = getActiveInput_l();
    while (spIn != 0) {
        int cnt = spIn->prepareLock();
        mLock.unlock();
        spIn->lock();
        mLock.lock();
        // make sure that another thread did not change input state while the
        // mutex is released
        if ((spIn == getActiveInput_l()) && (cnt == spIn->standbyCnt())) {
            break;
        }
        spIn->unlock();
        spIn = getActiveInput_l();
    }
    // spIn is not 0 here only if the input is active

    int prevMode = mMode;
    status = AudioHardwareBase::setMode(mode);
    ALOGV("setMode() : new %d, old %d", mMode, prevMode);
    if (status == NO_ERROR) {
        bool modeNeedsCPActive = mMode == AudioSystem::MODE_IN_CALL ||
                                    mMode == AudioSystem::MODE_RINGTONE;
        // activate call clock in radio when entering in call or ringtone mode
        if (modeNeedsCPActive)
        {
            if ((!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK)) {
                setCallClockSync(mRilClient, SOUND_CLOCK_START);
                mActivatedCP = true;
            }
        }

        if (mMode == AudioSystem::MODE_IN_CALL && !mInCallAudioMode) {
            if (spOut != 0) {
                ALOGV("setMode() in call force output standby");
                spOut->doStandby_l();
            }
            if (spIn != 0) {
                ALOGV("setMode() in call force input standby");
                spIn->doStandby_l();
            }

            ALOGV("setMode() openPcmOut_l()");
            openPcmOut_l();
            openMixer_l();
            setInputSource_l(AUDIO_SOURCE_DEFAULT);
            setVoiceVolume_l(mVoiceVol);
            mInCallAudioMode = true;
        }
        if (mMode != AudioSystem::MODE_IN_CALL && mInCallAudioMode) {
            setInputSource_l(mInputSource);
            if (mMixer != NULL) {
                TRACE_DRIVER_IN(DRV_MIXER_GET)
                struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Playback Path");
                TRACE_DRIVER_OUT
                if (ctl != NULL) {
                    ALOGV("setMode() reset Playback Path to RCV");
                    TRACE_DRIVER_IN(DRV_MIXER_SEL)
                    mixer_ctl_set_enum_by_string(ctl, "RCV");
                    TRACE_DRIVER_OUT
                }
            }
            ALOGV("setMode() closePcmOut_l()");
            closeMixer_l();
            closePcmOut_l();

            if (spOut != 0) {
                ALOGV("setMode() off call force output standby");
                spOut->doStandby_l();
            }
            if (spIn != 0) {
                ALOGV("setMode() off call force input standby");
                spIn->doStandby_l();
            }

            mInCallAudioMode = false;
        }

        if (!modeNeedsCPActive) {
            if(mActivatedCP)
                mActivatedCP = false;
        }
    }

    if (spIn != 0) {
        spIn->unlock();
    }
    if (spOut != 0) {
        spOut->unlock();
    }

    return status;
}

status_t AudioHardware::setMicMute(bool state)
{
    ALOGV("setMicMute(%d) mMicMute %d", state, mMicMute);
    sp<AudioStreamInALSA> spIn;
    {
        AutoMutex lock(mLock);
        if (mMicMute != state) {
            mMicMute = state;
            // in call mute is handled by RIL
            if (mMode != AudioSystem::MODE_IN_CALL) {
                spIn = getActiveInput_l();
            }
        }
    }

    if (spIn != 0) {
        spIn->standby();
    }

    return NO_ERROR;
}

status_t AudioHardware::getMicMute(bool* state)
{
    *state = mMicMute;
    return NO_ERROR;
}

status_t AudioHardware::setParameters(const String8& keyValuePairs)
{
    AudioParameter param = AudioParameter(keyValuePairs);
    String8 value;
    String8 key;
    const char BT_NREC_KEY[] = "bt_headset_nrec";
    const char BT_NREC_VALUE_ON[] = "on";
    const char TTY_MODE_KEY[] = "tty_mode";
    const char TTY_MODE_VALUE_OFF[] = "tty_off";
    const char TTY_MODE_VALUE_VCO[] = "tty_vco";
    const char TTY_MODE_VALUE_HCO[] = "tty_hco";
    const char TTY_MODE_VALUE_FULL[] = "tty_full";

    key = String8(BT_NREC_KEY);
    if (param.get(key, value) == NO_ERROR) {
        if (value == BT_NREC_VALUE_ON) {
            mBluetoothNrec = true;
        } else {
            mBluetoothNrec = false;
            ALOGD("Turning noise reduction and echo cancellation off for BT "
                 "headset");
        }
        param.remove(String8(BT_NREC_KEY));
    }

    key = String8(TTY_MODE_KEY);
    if (param.get(key, value) == NO_ERROR) {
        int ttyMode;
        if (value == TTY_MODE_VALUE_OFF) {
            ttyMode = TTY_MODE_OFF;
        } else if (value == TTY_MODE_VALUE_VCO) {
            ttyMode = TTY_MODE_VCO;
        } else if (value == TTY_MODE_VALUE_HCO) {
            ttyMode = TTY_MODE_HCO;
        } else if (value == TTY_MODE_VALUE_FULL) {
            ttyMode = TTY_MODE_FULL;
        } else {
            return BAD_VALUE;
        }

        if (ttyMode != mTTYMode) {
            ALOGV("new tty mode %d", ttyMode);
            mTTYMode = ttyMode;
            if (mOutput != 0 && mMode == AudioSystem::MODE_IN_CALL) {
                setIncallPath_l(mOutput->device());
            }
        }
        param.remove(String8(TTY_MODE_KEY));
     }

    return NO_ERROR;
}

String8 AudioHardware::getParameters(const String8& keys)
{
    AudioParameter request = AudioParameter(keys);
    AudioParameter reply = AudioParameter();

    ALOGV("getParameters() %s", keys.string());

    return reply.toString();
}

size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
    if (format != AudioSystem::PCM_16_BIT) {
        ALOGW("getInputBufferSize bad format: %d", format);
        return 0;
    }
    if (channelCount < 1 || channelCount > 2) {
        ALOGW("getInputBufferSize bad channel count: %d", channelCount);
        return 0;
    }

    if (sampleRate != getInputSampleRate(sampleRate)) {
        ALOGW("getInputBufferSize bad sample rate: %d", sampleRate);
        return 0;
    }

    return AudioStreamInALSA::getBufferSize(sampleRate, channelCount);
}

status_t AudioHardware::setVoiceVolume(float volume)
{
    AutoMutex lock(mLock);

    setVoiceVolume_l(volume);

    return NO_ERROR;
}

void AudioHardware::setVoiceVolume_l(float volume)
{
    ALOGD("### setVoiceVolume_l");

    mVoiceVol = volume;

    if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) &&
         (connectRILDIfRequired() == OK) ) {

        uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE;
        if (mOutput != 0) {
            device = mOutput->device();
        }
        int int_volume = (int)(volume * 5);
        SoundType type;

        ALOGD("### route(%d) call volume(%f)", device, volume);
        switch (device) {
            case AudioSystem::DEVICE_OUT_EARPIECE:
                ALOGD("### earpiece call volume");
                type = SOUND_TYPE_VOICE;
                break;

            case AudioSystem::DEVICE_OUT_SPEAKER:
                ALOGD("### speaker call volume");
                type = SOUND_TYPE_SPEAKER;
                break;

            case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
            case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
            case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
                ALOGD("### bluetooth call volume");
                type = SOUND_TYPE_BTVOICE;
                break;

            case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
            case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: // Use receive path with 3 pole headset.
                ALOGD("### headset call volume");
                type = SOUND_TYPE_HEADSET;
                break;

            default:
                ALOGW("### Call volume setting error!!!0x%08x \n", device);
                type = SOUND_TYPE_VOICE;
                break;
        }
        setCallVolume(mRilClient, type, int_volume);
    }

}

status_t AudioHardware::setMasterVolume(float volume)
{
    ALOGV("Set master volume to %f.\n", volume);
    // We return an error code here to let the audioflinger do in-software
    // volume on top of the maximum volume that we set through the SND API.
    // return error - software mixer will handle it
    return -1;
}

static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 20000;

static bool tryLock(Mutex& mutex)
{
    bool locked = false;
    for (int i = 0; i < kDumpLockRetries; ++i) {
        if (mutex.tryLock() == NO_ERROR) {
            locked = true;
            break;
        }
        usleep(kDumpLockSleep);
    }
    return locked;
}

status_t AudioHardware::dump(int fd, const Vector<String16>& args)
{
    const size_t SIZE = 256;
    char buffer[SIZE];
    String8 result;

    bool locked = tryLock(mLock);
    if (!locked) {
        snprintf(buffer, SIZE, "\n\tAudioHardware maybe deadlocked\n");
    } else {
        mLock.unlock();
    }

    snprintf(buffer, SIZE, "\tInit %s\n", (mInit) ? "OK" : "Failed");
    result.append(buffer);
    snprintf(buffer, SIZE, "\tMic Mute %s\n", (mMicMute) ? "ON" : "OFF");
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmPcm: %p\n", mPcm);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmPcmOpenCnt: %d\n", mPcmOpenCnt);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmMixer: %p\n", mMixer);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmMixerOpenCnt: %d\n", mMixerOpenCnt);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tIn Call Audio Mode %s\n",
             (mInCallAudioMode) ? "ON" : "OFF");
    result.append(buffer);
    snprintf(buffer, SIZE, "\tInput source %d\n", mInputSource);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmSecRilLibHandle: %p\n", mSecRilLibHandle);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmRilClient: %p\n", mRilClient);
    result.append(buffer);
    snprintf(buffer, SIZE, "\tCP %s\n",
             (mActivatedCP) ? "Activated" : "Deactivated");
    result.append(buffer);
    snprintf(buffer, SIZE, "\tmDriverOp: %d\n", mDriverOp);
    result.append(buffer);

    snprintf(buffer, SIZE, "\n\tmOutput %p dump:\n", mOutput.get());
    result.append(buffer);
    write(fd, result.string(), result.size());
    if (mOutput != 0) {
        mOutput->dump(fd, args);
    }

    snprintf(buffer, SIZE, "\n\t%d inputs opened:\n", mInputs.size());
    write(fd, buffer, strlen(buffer));
    for (size_t i = 0; i < mInputs.size(); i++) {
        snprintf(buffer, SIZE, "\t- input %d dump:\n", i);
        write(fd, buffer, strlen(buffer));
        mInputs[i]->dump(fd, args);
    }

    return NO_ERROR;
}

status_t AudioHardware::setIncallPath_l(uint32_t device)
{
    ALOGV("setIncallPath_l: device %x", device);

    // Setup sound path for CP clocking
    if ((mSecRilLibHandle) &&
        (connectRILDIfRequired() == OK)) {

        if (mMode == AudioSystem::MODE_IN_CALL) {
            ALOGD("### incall mode route (%d)", device);
            AudioPath path;
            switch(device){
                case AudioSystem::DEVICE_OUT_EARPIECE:
                    ALOGD("### incall mode earpiece route");
                    path = SOUND_AUDIO_PATH_HANDSET;
                    break;

                case AudioSystem::DEVICE_OUT_SPEAKER:
                    ALOGD("### incall mode speaker route");
                    path = SOUND_AUDIO_PATH_SPEAKER;
                    break;

                case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
                case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
                case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
                    ALOGD("### incall mode bluetooth route %s NR", mBluetoothNrec ? "" : "NO");
                    if (mBluetoothNrec) {
                        path = SOUND_AUDIO_PATH_BLUETOOTH;
                    } else {
                        path = SOUND_AUDIO_PATH_BLUETOOTH_NO_NR;
                    }
                    break;

                case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE :
                    ALOGD("### incall mode headphone route");
                    path = SOUND_AUDIO_PATH_HEADPHONE;
                    break;
                case AudioSystem::DEVICE_OUT_WIRED_HEADSET :
                    ALOGD("### incall mode headset route");
                    path = SOUND_AUDIO_PATH_HEADSET;
                    break;
                default:
                    ALOGW("### incall mode Error!! route = [%d]", device);
                    path = SOUND_AUDIO_PATH_HANDSET;
                    break;
            }

            setCallAudioPath(mRilClient, path);

            if (mMixer != NULL) {
                TRACE_DRIVER_IN(DRV_MIXER_GET)
                struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Voice Call Path");
                TRACE_DRIVER_OUT
                ALOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl");
                if (ctl != NULL) {
                    ALOGV("setIncallPath_l() Voice Call Path, (%x)", device);
                    TRACE_DRIVER_IN(DRV_MIXER_SEL)
                    mixer_ctl_set_enum_by_string(ctl, getVoiceRouteFromDevice(device));
                    TRACE_DRIVER_OUT
                }
            }
        }
    }
    return NO_ERROR;
}

struct pcm *AudioHardware::openPcmOut_l()
{
    ALOGD("openPcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
    if (mPcmOpenCnt++ == 0) {
        if (mPcm != NULL) {
            ALOGE("openPcmOut_l() mPcmOpenCnt == 0 and mPcm == %p\n", mPcm);
            mPcmOpenCnt--;
            return NULL;
        }
        unsigned flags = PCM_OUT;

        struct pcm_config config = {
            channels : 2,
            rate : AUDIO_HW_OUT_SAMPLERATE,
            period_size : AUDIO_HW_OUT_PERIOD_SZ,
            period_count : AUDIO_HW_OUT_PERIOD_CNT,
            format : PCM_FORMAT_S16_LE,
            start_threshold : 0,
            stop_threshold : 0,
            silence_threshold : 0,
            avail_min : 0,
        };

        TRACE_DRIVER_IN(DRV_PCM_OPEN)
        mPcm = pcm_open(0, 0, flags, &config);
        TRACE_DRIVER_OUT
        if (!pcm_is_ready(mPcm)) {
            ALOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_get_error(mPcm));
            TRACE_DRIVER_IN(DRV_PCM_CLOSE)
            pcm_close(mPcm);
            TRACE_DRIVER_OUT
            mPcmOpenCnt--;
            mPcm = NULL;
        }
    }
    return mPcm;
}

void AudioHardware::closePcmOut_l()
{
    ALOGD("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
    if (mPcmOpenCnt == 0) {
        ALOGE("closePcmOut_l() mPcmOpenCnt == 0");
        return;
    }

    if (--mPcmOpenCnt == 0) {
        TRACE_DRIVER_IN(DRV_PCM_CLOSE)
        pcm_close(mPcm);
        TRACE_DRIVER_OUT
        mPcm = NULL;
    }
}

struct mixer *AudioHardware::openMixer_l()
{
    ALOGV("openMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
    if (mMixerOpenCnt++ == 0) {
        if (mMixer != NULL) {
            ALOGE("openMixer_l() mMixerOpenCnt == 0 and mMixer == %p\n", mMixer);
            mMixerOpenCnt--;
            return NULL;
        }
        TRACE_DRIVER_IN(DRV_MIXER_OPEN)
        mMixer = mixer_open(0);
        TRACE_DRIVER_OUT
        if (mMixer == NULL) {
            ALOGE("openMixer_l() cannot open mixer");
            mMixerOpenCnt--;
            return NULL;
        }
    }
    return mMixer;
}

void AudioHardware::closeMixer_l()
{
    ALOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
    if (mMixerOpenCnt == 0) {
        ALOGE("closeMixer_l() mMixerOpenCnt == 0");
        return;
    }

    if (--mMixerOpenCnt == 0) {
        TRACE_DRIVER_IN(DRV_MIXER_CLOSE)
        mixer_close(mMixer);
        TRACE_DRIVER_OUT
        mMixer = NULL;
    }
}

const char *AudioHardware::getOutputRouteFromDevice(uint32_t device)
{
    switch (device) {
    case AudioSystem::DEVICE_OUT_EARPIECE:
        return "RCV";
    case AudioSystem::DEVICE_OUT_SPEAKER:
        if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK";
        else return "SPK";
    case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
        if (mMode == AudioSystem::MODE_RINGTONE) return "RING_NO_MIC";
        else return "HP_NO_MIC";
    case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
        if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP";
        else return "HP";
    case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADPHONE):
    case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADSET):
        if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK_HP";
        else return "SPK_HP";
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
        return "BT";
    default:
        return "OFF";
    }
}

const char *AudioHardware::getVoiceRouteFromDevice(uint32_t device)
{
    switch (device) {
    case AudioSystem::DEVICE_OUT_EARPIECE:
        return "RCV";
    case AudioSystem::DEVICE_OUT_SPEAKER:
        return "SPK";
    case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
    case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
        switch (mTTYMode) {
        case TTY_MODE_VCO:
            return "TTY_VCO";
        case TTY_MODE_HCO:
            return "TTY_HCO";
        case TTY_MODE_FULL:
            return "TTY_FULL";
        case TTY_MODE_OFF:
        default:
            if (device == AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
                return "HP_NO_MIC";
            } else {
                return "HP";
            }
        }
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
    case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
        return "BT";
    default:
        return "OFF";
    }
}

const char *AudioHardware::getInputRouteFromDevice(uint32_t device)
{
    if (mMicMute) {
        return "MIC OFF";
    }

    switch (device) {
    case AudioSystem::DEVICE_IN_BUILTIN_MIC:
        return "Main Mic";
    case AudioSystem::DEVICE_IN_WIRED_HEADSET:
        return "Hands Free Mic";
    case AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET:
        return "BT Sco Mic";
    default:
        return "MIC OFF";
    }
}

uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate)
{
    size_t i;
    uint32_t prevDelta;
    uint32_t delta;
    size_t size = sizeof(inputConfigTable)/sizeof(uint32_t)/INPUT_CONFIG_CNT;

    for (i = 0, prevDelta = 0xFFFFFFFF; i < size; i++, prevDelta = delta) {
        delta = abs(sampleRate - inputConfigTable[i][INPUT_CONFIG_SAMPLE_RATE]);
        if (delta > prevDelta) break;
    }
    // i is always > 0 here
    return inputConfigTable[i-1][INPUT_CONFIG_SAMPLE_RATE];
}

// getActiveInput_l() must be called with mLock held
sp <AudioHardware::AudioStreamInALSA> AudioHardware::getActiveInput_l()
{
    sp< AudioHardware::AudioStreamInALSA> spIn;

    for (size_t i = 0; i < mInputs.size(); i++) {
        // return first input found not being in standby mode
        // as only one input can be in this state
        if (!mInputs[i]->checkStandby()) {
            spIn = mInputs[i];
            break;
        }
    }

    return spIn;
}

status_t AudioHardware::setInputSource_l(audio_source source)
{
     ALOGV("setInputSource_l(%d)", source);
     if (source != mInputSource) {
         if ((source == AUDIO_SOURCE_DEFAULT) || (mMode != AudioSystem::MODE_IN_CALL)) {
             if (mMixer) {
                 TRACE_DRIVER_IN(DRV_MIXER_GET)
                 struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Input Source");
                 TRACE_DRIVER_OUT
                 if (ctl == NULL) {
                     return NO_INIT;
                 }
                 const char* sourceName;
                 switch (source) {
                     case AUDIO_SOURCE_DEFAULT: // intended fall-through
                     case AUDIO_SOURCE_MIC:     // intended fall-through
                     case AUDIO_SOURCE_VOICE_COMMUNICATION:
                         sourceName = inputPathNameDefault;
                         break;
                     case AUDIO_SOURCE_CAMCORDER:
                         sourceName = inputPathNameCamcorder;
                         break;
                     case AUDIO_SOURCE_VOICE_RECOGNITION:
                         sourceName = inputPathNameVoiceRecognition;
                         break;
                     case AUDIO_SOURCE_VOICE_UPLINK:   // intended fall-through
                     case AUDIO_SOURCE_VOICE_DOWNLINK: // intended fall-through
                     case AUDIO_SOURCE_VOICE_CALL:     // intended fall-through
                     default:
                         return NO_INIT;
                 }
                 ALOGV("mixer_ctl_set_enum_by_string, Input Source, (%s)", sourceName);
                 TRACE_DRIVER_IN(DRV_MIXER_SEL)
                 mixer_ctl_set_enum_by_string(ctl, sourceName);
                 TRACE_DRIVER_OUT
             }
         }
         mInputSource = source;
     }

     return NO_ERROR;
}

struct echo_reference_itfe *AudioHardware::getEchoReference(audio_format_t format,
                                                              uint32_t channelCount,
                                                              uint32_t samplingRate)
{
    ALOGV("AudioHardware::getEchoReference %p", mEchoReference);
    releaseEchoReference(mEchoReference);
    if (mOutput != NULL) {
        uint32_t wrChannelCount = popcount(mOutput->channels());
        uint32_t wrSampleRate = mOutput->sampleRate();

        int status = create_echo_reference(AUDIO_FORMAT_PCM_16_BIT,
                              channelCount,
                              samplingRate,
                              AUDIO_FORMAT_PCM_16_BIT,
                              wrChannelCount,
                              wrSampleRate,
                              &mEchoReference);
        if (status == 0) {
            mOutput->addEchoReference(mEchoReference);
        }
    }
    return mEchoReference;
}

void AudioHardware::releaseEchoReference(struct echo_reference_itfe *reference)
{
    ALOGV("AudioHardware::releaseEchoReference %p", mEchoReference);
    if (mEchoReference != NULL && reference == mEchoReference) {
        if (mOutput != NULL) {
            mOutput->removeEchoReference(reference);
        }
        release_echo_reference(mEchoReference);
        mEchoReference = NULL;
    }
}


//------------------------------------------------------------------------------
//  AudioStreamOutALSA
//------------------------------------------------------------------------------

AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() :
    mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0),
    mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS),
    mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES),
    mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false), mEchoReference(NULL)
{
}

status_t AudioHardware::AudioStreamOutALSA::set(
    AudioHardware* hw, uint32_t devices, int *pFormat,
    uint32_t *pChannels, uint32_t *pRate)
{
    int lFormat = pFormat ? *pFormat : 0;
    uint32_t lChannels = pChannels ? *pChannels : 0;
    uint32_t lRate = pRate ? *pRate : 0;

    mHardware = hw;
    mDevices = devices;

    // fix up defaults
    if (lFormat == 0) lFormat = format();
    if (lChannels == 0) lChannels = channels();
    if (lRate == 0) lRate = sampleRate();

    // check values
    if ((lFormat != format()) ||
        (lChannels != channels()) ||
        (lRate != sampleRate())) {
        if (pFormat) *pFormat = format();
        if (pChannels) *pChannels = channels();
        if (pRate) *pRate = sampleRate();
        return BAD_VALUE;
    }

    if (pFormat) *pFormat = lFormat;
    if (pChannels) *pChannels = lChannels;
    if (pRate) *pRate = lRate;

    mChannels = lChannels;
    mSampleRate = lRate;
    mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES;

    return NO_ERROR;
}

AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA()
{
    standby();
}

int AudioHardware::AudioStreamOutALSA::getPlaybackDelay(size_t frames,
                                                        struct echo_reference_buffer *buffer)
{
    size_t kernelFr;

    int rc = pcm_get_htimestamp(mPcm, &kernelFr, &buffer->time_stamp);
    if (rc < 0) {
        buffer->time_stamp.tv_sec  = 0;
        buffer->time_stamp.tv_nsec = 0;
        buffer->delay_ns           = 0;
        ALOGV("getPlaybackDelay(): pcm_get_htimestamp error, setting playbackTimestamp to 0");
        return rc;
    }

    kernelFr = pcm_get_buffer_size(mPcm) - kernelFr;

    // adjust render time stamp with delay added by current driver buffer.
    // Add the duration of current frame as we want the render time of the last
    // sample being written.
    long delayNs = (long)(((int64_t)(kernelFr + frames)* 1000000000) /AUDIO_HW_OUT_SAMPLERATE);

    ALOGV("AudioStreamOutALSA::getPlaybackDelay delayNs: [%ld], "\
         "kernelFr:[%d], frames:[%d], buffSize:[%d], time_stamp:[%ld].[%ld]",
         delayNs, (int)kernelFr, (int)frames, pcm_get_buffer_size(mPcm),
         (long)buffer->time_stamp.tv_sec, buffer->time_stamp.tv_nsec);

    buffer->delay_ns = delayNs;

    return 0;
}

ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes)
{
    ALOGV("-----AudioStreamInALSA::write(%p, %d) START", buffer, (int)bytes);
    status_t status = NO_INIT;
    const uint8_t* p = static_cast<const uint8_t*>(buffer);
    int ret;

    if (mHardware == NULL) return NO_INIT;

    if (mSleepReq) {
        // 10ms are always shorter than the time to reconfigure the audio path
        // which is the only condition when mSleepReq would be true.
        usleep(10000);
    }

    { // scope for the lock

        AutoMutex lock(mLock);

        if (mStandby) {
            AutoMutex hwLock(mHardware->lock());

            ALOGD("AudioHardware pcm playback is exiting standby.");
            sp<AudioStreamInALSA> spIn = mHardware->getActiveInput_l();
            while (spIn != 0) {
                int cnt = spIn->prepareLock();
                mHardware->lock().unlock();
                // Mutex acquisition order is always out -> in -> hw
                spIn->lock();
                mHardware->lock().lock();
                // make sure that another thread did not change input state
                // while the mutex is released
                if ((spIn == mHardware->getActiveInput_l()) &&
                        (cnt == spIn->standbyCnt())) {
                    ALOGV("AudioStreamOutALSA::write() force input standby");
                    spIn->close_l();
                    break;
                }
                spIn->unlock();
                spIn = mHardware->getActiveInput_l();
            }
            // spIn is not 0 here only if the input was active and has been
            // closed above

            // open output before input
            open_l();

            if (spIn != 0) {
                if (spIn->open_l() != NO_ERROR) {
                    spIn->doStandby_l();
                }
                spIn->unlock();
            }
            if (mPcm == NULL) {
                goto Error;
            }
            mStandby = false;
        }

        if (mEchoReference != NULL) {
            struct echo_reference_buffer b;
            b.raw = (void *)buffer;
            b.frame_count = bytes / frameSize();

            getPlaybackDelay(bytes / frameSize(), &b);
            mEchoReference->write(mEchoReference, &b);
        }

        TRACE_DRIVER_IN(DRV_PCM_WRITE)
        ret = pcm_write(mPcm,(void*) p, bytes);
        TRACE_DRIVER_OUT

        if (ret == 0) {
            ALOGV("-----AudioStreamInALSA::write(%p, %d) END", buffer, (int)bytes);
            return bytes;
        }
        ALOGW("write error: %d", errno);
        status = -errno;
    }
Error:
    standby();

    // Simulate audio output timing in case of error
    usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate());
    ALOGE("AudioStreamOutALSA::write END WITH ERROR !!!!!!!!!(%p, %u)", buffer, bytes);
    return status;
}

status_t AudioHardware::AudioStreamOutALSA::standby()
{
    if (mHardware == NULL) return NO_INIT;

    mSleepReq = true;
    {
        AutoMutex lock(mLock);
        mSleepReq = false;

        { // scope for the AudioHardware lock
            AutoMutex hwLock(mHardware->lock());

            doStandby_l();
        }
    }

    return NO_ERROR;
}

void AudioHardware::AudioStreamOutALSA::doStandby_l()
{
    mStandbyCnt++;

    if (!mStandby) {
        ALOGD("AudioHardware pcm playback is going to standby.");
        // stop echo reference capture
        if (mEchoReference != NULL) {
            mEchoReference->write(mEchoReference, NULL);
        }
        mStandby = true;
    }

    close_l();
}

void AudioHardware::AudioStreamOutALSA::close_l()
{
    if (mMixer) {
        mHardware->closeMixer_l();
        mMixer = NULL;
        mRouteCtl = NULL;
    }
    if (mPcm) {
        mHardware->closePcmOut_l();
        mPcm = NULL;
    }
}

status_t AudioHardware::AudioStreamOutALSA::open_l()
{
    ALOGV("open pcm_out driver");
    mPcm = mHardware->openPcmOut_l();
    if (mPcm == NULL) {
        return NO_INIT;
    }

    mMixer = mHardware->openMixer_l();
    if (mMixer) {
        ALOGV("open playback normal");
        TRACE_DRIVER_IN(DRV_MIXER_GET)
        mRouteCtl = mixer_get_ctl_by_name(mMixer, "Playback Path");
        TRACE_DRIVER_OUT
    }
    if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
        const char *route = mHardware->getOutputRouteFromDevice(mDevices);
        ALOGV("write() wakeup setting route %s", route);
        if (mRouteCtl) {
            TRACE_DRIVER_IN(DRV_MIXER_SEL)
            mixer_ctl_set_enum_by_string(mRouteCtl, route);
            TRACE_DRIVER_OUT
        }
    }
    return NO_ERROR;
}

status_t AudioHardware::AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
{
    const size_t SIZE = 256;
    char buffer[SIZE];
    String8 result;

    bool locked = tryLock(mLock);
    if (!locked) {
        snprintf(buffer, SIZE, "\n\t\tAudioStreamOutALSA maybe deadlocked\n");
    } else {
        mLock.unlock();
    }

    snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmRouteCtl: %p\n", mRouteCtl);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF");
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp);
    result.append(buffer);

    ::write(fd, result.string(), result.size());

    return NO_ERROR;
}

bool AudioHardware::AudioStreamOutALSA::checkStandby()
{
    return mStandby;
}

status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs)
{
    AudioParameter param = AudioParameter(keyValuePairs);
    status_t status = NO_ERROR;
    int device;
    ALOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string());

    if (mHardware == NULL) return NO_INIT;

    mSleepReq = true;
    {
        AutoMutex lock(mLock);
        mSleepReq = false;
        if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR)
        {
            if (device != 0) {
                AutoMutex hwLock(mHardware->lock());

                if (mDevices != (uint32_t)device) {
                    mDevices = (uint32_t)device;
                    if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
                        doStandby_l();
                    }
                }
                if (mHardware->mode() == AudioSystem::MODE_IN_CALL) {
                    mHardware->setIncallPath_l(device);
                }
            }
            param.remove(String8(AudioParameter::keyRouting));
        }
    }

    if (param.size()) {
        status = BAD_VALUE;
    }


    return status;

}

String8 AudioHardware::AudioStreamOutALSA::getParameters(const String8& keys)
{
    AudioParameter param = AudioParameter(keys);
    String8 value;
    String8 key = String8(AudioParameter::keyRouting);

    if (param.get(key, value) == NO_ERROR) {
        param.addInt(key, (int)mDevices);
    }

    ALOGV("AudioStreamOutALSA::getParameters() %s", param.toString().string());
    return param.toString();
}

status_t AudioHardware::AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames)
{
    //TODO
    return INVALID_OPERATION;
}

int AudioHardware::AudioStreamOutALSA::prepareLock()
{
    // request sleep next time write() is called so that caller can acquire
    // mLock
    mSleepReq = true;
    return mStandbyCnt;
}

void AudioHardware::AudioStreamOutALSA::lock()
{
    mLock.lock();
    mSleepReq = false;
}

void AudioHardware::AudioStreamOutALSA::unlock() {
    mLock.unlock();
}

void AudioHardware::AudioStreamOutALSA::addEchoReference(struct echo_reference_itfe *reference)
{
    ALOGV("AudioStreamOutALSA::addEchoReference %p", mEchoReference);
    if (mEchoReference == NULL) {
        mEchoReference = reference;
    }
}

void AudioHardware::AudioStreamOutALSA::removeEchoReference(struct echo_reference_itfe *reference)
{
    ALOGV("AudioStreamOutALSA::removeEchoReference %p", mEchoReference);
    if (mEchoReference == reference) {
        mEchoReference->write(mEchoReference, NULL);
        mEchoReference = NULL;
    }
}


//------------------------------------------------------------------------------
//  AudioStreamInALSA
//------------------------------------------------------------------------------

AudioHardware::AudioStreamInALSA::AudioStreamInALSA() :
    mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0),
    mStandby(true), mDevices(0), mChannels(AUDIO_HW_IN_CHANNELS), mChannelCount(1),
    mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES),
    mDownSampler(NULL), mReadStatus(NO_ERROR), mInputBuf(NULL),
    mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false),
    mProcBuf(NULL), mProcBufSize(0), mRefBuf(NULL), mRefBufSize(0),
    mEchoReference(NULL), mNeedEchoReference(false)
{
}

status_t AudioHardware::AudioStreamInALSA::set(
    AudioHardware* hw, uint32_t devices, int *pFormat,
    uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics)
{
    if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) {
        *pFormat = AUDIO_HW_IN_FORMAT;
        return BAD_VALUE;
    }
    if (pRate == 0) {
        return BAD_VALUE;
    }
    uint32_t rate = AudioHardware::getInputSampleRate(*pRate);
    if (rate != *pRate) {
        *pRate = rate;
        return BAD_VALUE;
    }

    if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
        *pChannels != AudioSystem::CHANNEL_IN_STEREO)) {
        *pChannels = AUDIO_HW_IN_CHANNELS;
        return BAD_VALUE;
    }

    mHardware = hw;

    ALOGV("AudioStreamInALSA::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);

    mBufferSize = getBufferSize(*pRate, AudioSystem::popCount(*pChannels));
    mDevices = devices;
    mChannels = *pChannels;
    mChannelCount = AudioSystem::popCount(mChannels);
    mSampleRate = rate;
    if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) {
        mBufferProvider.mProvider.get_next_buffer = getNextBufferStatic;
        mBufferProvider.mProvider.release_buffer = releaseBufferStatic;
        mBufferProvider.mInputStream = this;
        int status = create_resampler(AUDIO_HW_OUT_SAMPLERATE,
                                                    mSampleRate,
                                                    mChannelCount,
                                                    RESAMPLER_QUALITY_VOIP,
                                                    &mBufferProvider.mProvider,
                                                    &mDownSampler);
        if (status != 0) {
            ALOGW("AudioStreamInALSA::set() downsampler init failed: %d", status);
            mDownSampler = NULL;
            return status;
        }
    }
    mInputBuf = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount];

    return NO_ERROR;
}

AudioHardware::AudioStreamInALSA::~AudioStreamInALSA()
{
    standby();

    if (mDownSampler != NULL) {
        release_resampler(mDownSampler);
    }
    delete[] mInputBuf;
    delete[] mProcBuf;
}

// readFrames() reads frames from kernel driver, down samples to capture rate if necessary
// and output the number of frames requested to the buffer specified
ssize_t AudioHardware::AudioStreamInALSA::readFrames(void* buffer, ssize_t frames)
{
    ssize_t framesWr = 0;
    while (framesWr < frames) {
        size_t framesRd = frames - framesWr;
        if (mDownSampler != NULL) {
            mDownSampler->resample_from_provider(mDownSampler,
                    (int16_t *)((char *)buffer + framesWr * frameSize()),
                    &framesRd);
        } else {
            struct resampler_buffer buf = {
                    { raw : NULL, },
                    frame_count : framesRd,
            };
            getNextBuffer(&buf);
            if (buf.raw != NULL) {
                memcpy((char *)buffer + framesWr * frameSize(),
                        buf.raw,
                        buf.frame_count * frameSize());
                framesRd = buf.frame_count;
            }
            releaseBuffer(&buf);
        }
        // mReadStatus is updated by getNextBuffer() also called by
        // mDownSampler->resample_from_provider()
        if (mReadStatus != 0) {
            return mReadStatus;
        }
        framesWr += framesRd;
    }
    return framesWr;
}

// processFrames() reads frames from kernel driver (via readFrames()), calls the active
// audio pre processings and output the number of frames requested to the buffer specified
ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t frames)
{
    ssize_t framesWr = 0;
    while (framesWr < frames) {
        // first reload enough frames at the end of process input buffer
        if (mProcFramesIn < (size_t)frames) {
            // expand process input buffer if necessary
            if (mProcBufSize < (size_t)frames) {
                mProcBufSize = (size_t)frames;
                mProcBuf = (int16_t *)realloc(mProcBuf,
                                              mProcBufSize * mChannelCount * sizeof(int16_t));
                ALOGV("processFrames(): mProcBuf %p size extended to %d frames",
                     mProcBuf, mProcBufSize);
            }
            ssize_t framesRd = readFrames(mProcBuf + mProcFramesIn * mChannelCount,
                                          frames - mProcFramesIn);
            if (framesRd < 0) {
                framesWr = framesRd;
                break;
            }
            mProcFramesIn += framesRd;
        }

        if (mEchoReference != NULL) {
            pushEchoReference(mProcFramesIn);
        }

        //inBuf.frameCount and outBuf.frameCount indicate respectively the maximum number of frames
        //to be consumed and produced by process()
        audio_buffer_t inBuf = {
                mProcFramesIn,
                {mProcBuf}
        };
        audio_buffer_t outBuf = {
                frames - framesWr,
                {(int16_t *)buffer + framesWr * mChannelCount}
        };

        for (size_t i = 0; i < mPreprocessors.size(); i++) {
            (*mPreprocessors[i])->process(mPreprocessors[i],
                                                   &inBuf,
                                                   &outBuf);
        }

        // process() has updated the number of frames consumed and produced in
        // inBuf.frameCount and outBuf.frameCount respectively
        // move remaining frames to the beginning of mProcBuf
        mProcFramesIn -= inBuf.frameCount;
        if (mProcFramesIn) {
            memcpy(mProcBuf,
                   mProcBuf + inBuf.frameCount * mChannelCount,
                   mProcFramesIn * mChannelCount * sizeof(int16_t));
        }

        // if not enough frames were passed to process(), read more and retry.
        if (outBuf.frameCount == 0) {
            continue;
        }
        framesWr += outBuf.frameCount;
    }
    return framesWr;
}

int32_t AudioHardware::AudioStreamInALSA::updateEchoReference(size_t frames)
{
    struct echo_reference_buffer b;
    b.delay_ns = 0;

    ALOGV("updateEchoReference1 START, frames = [%d], mRefFramesIn = [%d],  b.frame_count = [%d]",
         frames, mRefFramesIn, frames - mRefFramesIn);
    if (mRefFramesIn < frames) {
        if (mRefBufSize < frames) {
            mRefBufSize = frames;
            mRefBuf = (int16_t *)realloc(mRefBuf,
                                         mRefBufSize * mChannelCount * sizeof(int16_t));
        }

        b.frame_count = frames - mRefFramesIn;
        b.raw = (void *)(mRefBuf + mRefFramesIn * mChannelCount);

        getCaptureDelay(frames, &b);

        if (mEchoReference->read(mEchoReference, &b) == NO_ERROR)
        {
            mRefFramesIn += b.frame_count;
            ALOGV("updateEchoReference2: mRefFramesIn:[%d], mRefBufSize:[%d], "\
                 "frames:[%d], b.frame_count:[%d]", mRefFramesIn, mRefBufSize,frames,b.frame_count);
        }

    }else{
        ALOGV("updateEchoReference3: NOT enough frames to read ref buffer");
    }
    return b.delay_ns;
}

void AudioHardware::AudioStreamInALSA::pushEchoReference(size_t frames)
{
    // read frames from echo reference buffer and update echo delay
    // mRefFramesIn is updated with frames available in mRefBuf
    int32_t delayUs = (int32_t)(updateEchoReference(frames)/1000);

    if (mRefFramesIn < frames) {
        frames = mRefFramesIn;
    }

    audio_buffer_t refBuf = {
            frames,
            {mRefBuf}
    };

    for (size_t i = 0; i < mPreprocessors.size(); i++) {
        if ((*mPreprocessors[i])->process_reverse == NULL) {
            continue;
        }
        (*mPreprocessors[i])->process_reverse(mPreprocessors[i],
                                               &refBuf,
                                               NULL);
        setPreProcessorEchoDelay(mPreprocessors[i], delayUs);
    }

    mRefFramesIn -= refBuf.frameCount;
    if (mRefFramesIn) {
    ALOGV("pushEchoReference5: shifting mRefBuf down by = %d frames", mRefFramesIn);
        memcpy(mRefBuf,
               mRefBuf + refBuf.frameCount * mChannelCount,
               mRefFramesIn * mChannelCount * sizeof(int16_t));
    }
}

status_t AudioHardware::AudioStreamInALSA::setPreProcessorEchoDelay(effect_handle_t handle,
                                                                    int32_t delayUs)
{
    uint32_t buf[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
    effect_param_t *param = (effect_param_t *)buf;

    param->psize = sizeof(uint32_t);
    param->vsize = sizeof(uint32_t);
    *(uint32_t *)param->data = AEC_PARAM_ECHO_DELAY;
    *((int32_t *)param->data + 1) = delayUs;

    ALOGV("setPreProcessorEchoDelay: %d us", delayUs);

    return setPreprocessorParam(handle, param);
}

status_t AudioHardware::AudioStreamInALSA::setPreprocessorParam(effect_handle_t handle,
                                                                effect_param_t *param)
{
    uint32_t size = sizeof(int);
    uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize;

    status_t status = (*handle)->command(handle,
                                           EFFECT_CMD_SET_PARAM,
                                           sizeof (effect_param_t) + psize,
                                           param,
                                           &size,
                                           &param->status);
    if (status == NO_ERROR) {
        status = param->status;
    }
    return status;
}

void AudioHardware::AudioStreamInALSA::getCaptureDelay(size_t frames,
                                                       struct echo_reference_buffer *buffer)
{

    // read frames available in kernel driver buffer
    size_t kernelFr;
    struct timespec tstamp;

    if (pcm_get_htimestamp(mPcm, &kernelFr, &tstamp) < 0) {
        buffer->time_stamp.tv_sec  = 0;
        buffer->time_stamp.tv_nsec = 0;
        buffer->delay_ns           = 0;
        ALOGW("read getCaptureDelay(): pcm_htimestamp error");
        return;
    }

    // read frames available in audio HAL input buffer
    // add number of frames being read as we want the capture time of first sample in current
    // buffer
    long bufDelay = (long)(((int64_t)(mInputFramesIn + mProcFramesIn) * 1000000000)
                                    / AUDIO_HW_IN_SAMPLERATE);
    // add delay introduced by resampler
    long rsmpDelay = 0;
    if (mDownSampler) {
        rsmpDelay = mDownSampler->delay_ns(mDownSampler);
    }

    long kernelDelay = (long)(((int64_t)kernelFr * 1000000000) / AUDIO_HW_IN_SAMPLERATE);

    // correct capture time stamp
    long delayNs = kernelDelay + bufDelay + rsmpDelay;

    buffer->time_stamp = tstamp;
    buffer->delay_ns   = delayNs;
    ALOGV("AudioStreamInALSA::getCaptureDelay TimeStamp = [%ld].[%ld], delayCaptureNs: [%d],"\
         " kernelDelay:[%ld], bufDelay:[%ld], rsmpDelay:[%ld], kernelFr:[%d], "\
         "mInputFramesIn:[%d], mProcFramesIn:[%d], frames:[%d]",
         buffer->time_stamp.tv_sec , buffer->time_stamp.tv_nsec, buffer->delay_ns,
         kernelDelay, bufDelay, rsmpDelay, kernelFr, mInputFramesIn, mProcFramesIn, frames);

}

ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes)
{
    ALOGV("-----AudioStreamInALSA::read(%p, %d) START", buffer, (int)bytes);
    status_t status = NO_INIT;

    if (mHardware == NULL) return NO_INIT;

    if (mSleepReq) {
        // 10ms are always shorter than the time to reconfigure the audio path
        // which is the only condition when mSleepReq would be true.
        usleep(10000);
    }

    { // scope for the lock
        AutoMutex lock(mLock);

        if (mStandby) {
            AutoMutex hwLock(mHardware->lock());

            ALOGD("AudioHardware pcm capture is exiting standby.");
            sp<AudioStreamOutALSA> spOut = mHardware->output();
            while (spOut != 0) {
                spOut->prepareLock();
                mHardware->lock().unlock();
                mLock.unlock();
                // Mutex acquisition order is always out -> in -> hw
                spOut->lock();
                mLock.lock();
                mHardware->lock().lock();
                // make sure that another thread did not change output state
                // while the mutex is released
                if (spOut == mHardware->output()) {
                    break;
                }
                spOut->unlock();
                spOut = mHardware->output();
            }
            // open output before input
            if (spOut != 0) {
                if (!spOut->checkStandby()) {
                    ALOGV("AudioStreamInALSA::read() force output standby");
                    spOut->close_l();
                    if (spOut->open_l() != NO_ERROR) {
                        spOut->doStandby_l();
                    }
                }
                ALOGV("AudioStreamInALSA exit standby mNeedEchoReference %d mEchoReference %p",
                     mNeedEchoReference, mEchoReference);
                if (mNeedEchoReference && mEchoReference == NULL) {
                    mEchoReference = mHardware->getEchoReference(AUDIO_FORMAT_PCM_16_BIT,
                                                                 mChannelCount,
                                                                 mSampleRate);
                }
                spOut->unlock();
            }

            open_l();

            if (mPcm == NULL) {
                goto Error;
            }
            mStandby = false;
        }

        size_t framesRq = bytes / mChannelCount/sizeof(int16_t);
        ssize_t framesRd;

        if (mPreprocessors.size() == 0) {
            framesRd = readFrames(buffer, framesRq);
        } else {
            framesRd = processFrames(buffer, framesRq);
        }

        if (framesRd >= 0) {
            ALOGV("-----AudioStreamInALSA::read(%p, %d) END", buffer, (int)bytes);
            return framesRd * mChannelCount * sizeof(int16_t);
        }

        ALOGW("read error: %d", (int)framesRd);
        status = framesRd;
    }

Error:

    standby();

    // Simulate audio output timing in case of error
    usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate());
    ALOGE("-----AudioStreamInALSA::read(%p, %d) END ERROR", buffer, (int)bytes);
    return status;
}

status_t AudioHardware::AudioStreamInALSA::standby()
{
    if (mHardware == NULL) return NO_INIT;

    mSleepReq = true;
    {
        AutoMutex lock(mLock);
        mSleepReq = false;

        { // scope for AudioHardware lock
            AutoMutex hwLock(mHardware->lock());

            doStandby_l();
        }
    }
    return NO_ERROR;
}

void AudioHardware::AudioStreamInALSA::doStandby_l()
{
    mStandbyCnt++;

    if (!mStandby) {
        ALOGD("AudioHardware pcm capture is going to standby.");
        if (mEchoReference != NULL) {
            // stop reading from echo reference
            mEchoReference->read(mEchoReference, NULL);
            // Mutex acquisition order is always out -> in -> hw
            sp<AudioStreamOutALSA> spOut = mHardware->output();
            if (spOut != 0) {
                spOut->prepareLock();
                mHardware->lock().unlock();
                mLock.unlock();
                spOut->lock();
                mLock.lock();
                mHardware->lock().lock();
                mHardware->releaseEchoReference(mEchoReference);
                spOut->unlock();
            }
            mEchoReference = NULL;
        }

        mStandby = true;
    }
    close_l();
}

void AudioHardware::AudioStreamInALSA::close_l()
{
    if (mMixer) {
        mHardware->closeMixer_l();
        mMixer = NULL;
        mRouteCtl = NULL;
    }

    if (mPcm) {
        TRACE_DRIVER_IN(DRV_PCM_CLOSE)
        pcm_close(mPcm);
        TRACE_DRIVER_OUT
        mPcm = NULL;
    }

    delete[] mProcBuf;
    mProcBuf = NULL;
    mProcBufSize = 0;
    delete[] mRefBuf;
    mRefBuf = NULL;
    mRefBufSize = 0;
}

status_t AudioHardware::AudioStreamInALSA::open_l()
{
    unsigned flags = PCM_IN;

    struct pcm_config config = {
        channels : mChannelCount,
        rate : AUDIO_HW_IN_SAMPLERATE,
        period_size : AUDIO_HW_IN_PERIOD_SZ,
        period_count : AUDIO_HW_IN_PERIOD_CNT,
        format : PCM_FORMAT_S16_LE,
        start_threshold : 0,
        stop_threshold : 0,
        silence_threshold : 0,
        avail_min : 0,
    };

    ALOGV("open pcm_in driver");
    TRACE_DRIVER_IN(DRV_PCM_OPEN)
    mPcm = pcm_open(0, 0, flags, &config);
    TRACE_DRIVER_OUT
    if (!pcm_is_ready(mPcm)) {
        ALOGE("cannot open pcm_in driver: %s\n", pcm_get_error(mPcm));
        TRACE_DRIVER_IN(DRV_PCM_CLOSE)
        pcm_close(mPcm);
        TRACE_DRIVER_OUT
        mPcm = NULL;
        return NO_INIT;
    }

    if (mDownSampler != NULL) {
        mDownSampler->reset(mDownSampler);
    }
    mInputFramesIn = 0;

    mProcBufSize = 0;
    mProcFramesIn = 0;
    mRefBufSize = 0;
    mRefFramesIn = 0;

    mMixer = mHardware->openMixer_l();
    if (mMixer) {
        TRACE_DRIVER_IN(DRV_MIXER_GET)
        mRouteCtl = mixer_get_ctl_by_name(mMixer, "Capture MIC Path");
        TRACE_DRIVER_OUT
    }

    if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
        const char *route = mHardware->getInputRouteFromDevice(mDevices);
        ALOGV("read() wakeup setting route %s", route);
        if (mRouteCtl) {
            TRACE_DRIVER_IN(DRV_MIXER_SEL)
            mixer_ctl_set_enum_by_string(mRouteCtl, route);
            TRACE_DRIVER_OUT
        }
    }

    return NO_ERROR;
}

status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
{
    const size_t SIZE = 256;
    char buffer[SIZE];
    String8 result;

    bool locked = tryLock(mLock);
    if (!locked) {
        snprintf(buffer, SIZE, "\n\t\tAudioStreamInALSA maybe deadlocked\n");
    } else {
        mLock.unlock();
    }

    snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF");
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize);
    result.append(buffer);
    snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp);
    result.append(buffer);
    write(fd, result.string(), result.size());

    return NO_ERROR;
}

bool AudioHardware::AudioStreamInALSA::checkStandby()
{
    return mStandby;
}

status_t AudioHardware::AudioStreamInALSA::setParameters(const String8& keyValuePairs)
{
    AudioParameter param = AudioParameter(keyValuePairs);
    status_t status = NO_ERROR;
    int value;

    ALOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string());

    if (mHardware == NULL) return NO_INIT;

    mSleepReq = true;
    {
        AutoMutex lock(mLock);
        mSleepReq = false;

        if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR) {
            AutoMutex hwLock(mHardware->lock());

            mHardware->openMixer_l();
            mHardware->setInputSource_l((audio_source)value);
            mHardware->closeMixer_l();

            param.remove(String8(AudioParameter::keyInputSource));
        }

        if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR)
        {
            if (value != 0) {
                AutoMutex hwLock(mHardware->lock());

                if (mDevices != (uint32_t)value) {
                    mDevices = (uint32_t)value;
                    if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
                        doStandby_l();
                    }
                }
            }
            param.remove(String8(AudioParameter::keyRouting));
        }
    }


    if (param.size()) {
        status = BAD_VALUE;
    }

    return status;

}

String8 AudioHardware::AudioStreamInALSA::getParameters(const String8& keys)
{
    AudioParameter param = AudioParameter(keys);
    String8 value;
    String8 key = String8(AudioParameter::keyRouting);

    if (param.get(key, value) == NO_ERROR) {
        param.addInt(key, (int)mDevices);
    }

    ALOGV("AudioStreamInALSA::getParameters() %s", param.toString().string());
    return param.toString();
}

status_t AudioHardware::AudioStreamInALSA::addAudioEffect(effect_handle_t effect)
{
    ALOGV("AudioStreamInALSA::addAudioEffect() %p", effect);

    effect_descriptor_t desc;
    status_t status = (*effect)->get_descriptor(effect, &desc);
    if (status == 0) {
        if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
            ALOGV("AudioStreamInALSA::addAudioEffect() mNeedEchoReference true");
            mNeedEchoReference = true;
            standby();
        }
        ALOGV("AudioStreamInALSA::addAudioEffect() name %s", desc.name);
    } else {
        ALOGV("AudioStreamInALSA::addAudioEffect() get_descriptor() error");
    }

    AutoMutex lock(mLock);
    mPreprocessors.add(effect);
    return NO_ERROR;
}

status_t AudioHardware::AudioStreamInALSA::removeAudioEffect(effect_handle_t effect)
{
    status_t status = INVALID_OPERATION;
    ALOGV("AudioStreamInALSA::removeAudioEffect() %p", effect);
    {
        AutoMutex lock(mLock);
        for (size_t i = 0; i < mPreprocessors.size(); i++) {
            if (mPreprocessors[i] == effect) {
                mPreprocessors.removeAt(i);
                status = NO_ERROR;
                break;
            }
        }
    }

    if (status == NO_ERROR) {
        effect_descriptor_t desc;
        if ((*effect)->get_descriptor(effect, &desc) == 0) {
            if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
                ALOGV("AudioStreamInALSA::removeAudioEffect() mNeedEchoReference false");
                mNeedEchoReference = false;
                standby();
            }
        }
    }

    return status;
}

extern "C" {
int AudioHardware::AudioStreamInALSA::getNextBufferStatic(
                                                    struct resampler_buffer_provider *provider,
                                                    struct resampler_buffer* buffer)
{
    ResamplerBufferProvider *bufferProvider = (ResamplerBufferProvider *)provider;
    return bufferProvider->mInputStream->getNextBuffer(buffer);
}

void AudioHardware::AudioStreamInALSA::releaseBufferStatic(
                                                    struct resampler_buffer_provider *provider,
                                                    struct resampler_buffer* buffer)
{
    ResamplerBufferProvider *bufferProvider = (ResamplerBufferProvider *)provider;
    return bufferProvider->mInputStream->releaseBuffer(buffer);
}

}; // extern "C"

status_t AudioHardware::AudioStreamInALSA::getNextBuffer(struct resampler_buffer *buffer)
{
    if (mPcm == NULL) {
        buffer->raw = NULL;
        buffer->frame_count = 0;
        mReadStatus = NO_INIT;
        return NO_INIT;
    }

    if (mInputFramesIn == 0) {
        TRACE_DRIVER_IN(DRV_PCM_READ)
        mReadStatus = pcm_read(mPcm,(void*) mInputBuf, AUDIO_HW_IN_PERIOD_SZ * frameSize());
        TRACE_DRIVER_OUT
        if (mReadStatus != 0) {
            buffer->raw = NULL;
            buffer->frame_count = 0;
            return mReadStatus;
        }
        mInputFramesIn = AUDIO_HW_IN_PERIOD_SZ;
    }

    buffer->frame_count = (buffer->frame_count > mInputFramesIn) ? mInputFramesIn:buffer->frame_count;
    buffer->i16 = mInputBuf + (AUDIO_HW_IN_PERIOD_SZ - mInputFramesIn) * mChannelCount;

    return mReadStatus;
}

void AudioHardware::AudioStreamInALSA::releaseBuffer(struct resampler_buffer *buffer)
{
    mInputFramesIn -= buffer->frame_count;
}

size_t AudioHardware::AudioStreamInALSA::getBufferSize(uint32_t sampleRate, int channelCount)
{
    size_t i;
    size_t size = sizeof(inputConfigTable)/sizeof(uint32_t)/INPUT_CONFIG_CNT;

    for (i = 0; i < size; i++) {
        if (sampleRate == inputConfigTable[i][INPUT_CONFIG_SAMPLE_RATE]) {
            return (AUDIO_HW_IN_PERIOD_SZ*channelCount*sizeof(int16_t)) /
                    inputConfigTable[i][INPUT_CONFIG_BUFFER_RATIO];
        }
    }
    // this should never happen as getBufferSize() is always called after getInputSampleRate()
    // that checks for valid sampling rates.
    ALOGE("AudioStreamInALSA::getBufferSize() invalid sampling rate %d", sampleRate);
    return 0;
}

int AudioHardware::AudioStreamInALSA::prepareLock()
{
    // request sleep next time read() is called so that caller can acquire
    // mLock
    mSleepReq = true;
    return mStandbyCnt;
}

void AudioHardware::AudioStreamInALSA::lock()
{
    mLock.lock();
    mSleepReq = false;
}

void AudioHardware::AudioStreamInALSA::unlock() {
    mLock.unlock();
}

//------------------------------------------------------------------------------
//  Factory
//------------------------------------------------------------------------------

extern "C" AudioHardwareInterface* createAudioHardware(void) {
    return new AudioHardware();
}

}; // namespace android