Commits

John Floren  committed 75008e7

moved android/ to android-tools/ because OS X sucks at case-sensitivity.

  • Participants
  • Parent commits 11c7ad6

Comments (0)

Files changed (34)

File android-tools/README

+To make your Nexus S boot into Inferno automatically, follow these steps:
+
+1. Turn on the phone and connect it to your computer
+2. Run Reflash-Nexus-S.sh
+3. When the script completes, press the Volume Down button to select
+   "REBOOT" and press the Power button.

File android-tools/Reflash-Nexus-S.sh

+#!/bin/sh
+
+echo "This script will automatically modify your hardware! Use with caution!"
+echo "After running this script, your phone should automatically boot into Inferno, if you have /data/inferno installed"
+echo "If you do not wish to take this step, press Ctrl-C in the next 10 seconds"
+echo "Waiting 10 seconds..."
+sleep 5
+echo "Waiting 5 seconds..."
+sleep 5
+echo "Going for it!"
+echo Grabbing the current boot image...
+adb shell "cat /dev/mtd/mtd2 > /data/boot.img"
+adb pull /data/boot.img work/boot.img
+
+echo Pushing the startup script
+adb shell "mount -o remount,rw /system"
+adb push ./inferno.sh /system/bin/inferno.sh
+
+echo Pushing the boot chooser
+adb push ./bootpicker/picker /data/picker
+
+echo Pushing the audio libraries
+adb push audio/libaudioflinger_inferno.so /system/lib/
+adb push audio/libinfernoaudio.so /system/lib
+
+echo Pushing the custom mediaserver
+adb push audio/mediaserver-inferno /system/bin
+
+echo Unpacking, modifying, and repacking the boot image
+cd work
+../unpack-bootimg.pl boot.img
+cp ../init.rc boot.img-ramdisk/init.rc
+../repack-bootimg.pl boot.img-kernel.gz boot.img-ramdisk boot-inferno.img
+
+echo Rebooting into the bootloader
+adb reboot-bootloader
+echo Flashing the ROM
+fastboot flash boot boot-inferno.img
+
+echo Cleaning up. Will leave boot-inferno.img just in case.
+rm -r boot.img*

File android-tools/Reflash-Nook-Color.sh

+#!/bin/sh
+
+adb shell "mount -o remount,rw /"
+adb shell "mount -o remount,rw /system"
+
+echo Grabbing ramdisk
+adb shell "mkdir /boot"
+adb shell "mount /dev/block/mmcblk0p1 /boot"
+adb pull /boot/uRamdisk work/uRamdisk
+
+echo Unpacking ramdisk
+cd work
+mkdir disk
+dd bs=1 if=uRamdisk of=disk/uRamdisk.gz skip=64
+cd disk
+gunzip -c uRamdisk.gz | cpio -i
+rm uRamdisk.gz
+
+echo Bringing in inferno-enabled init.rc
+cp ../../init.rc init.rc
+
+echo Repacking ramdisk
+find . -regex "./.*"| cpio -ov -H newc | gzip > ../repacked-ramdisk.gz
+../../mkimage  -A ARM -T RAMDisk -n Image -d ../repacked-ramdisk.gz ../uRamdisk
+cd ..
+rm repacked-ramdisk.gz
+
+echo Pushing the boot chooser
+adb push ../bootpicker/picker /data/picker
+
+echo Pushing inferno.sh to device
+adb push ../inferno.sh /system/bin/inferno.sh
+adb push uRamdisk /boot/uRamdisk
+
+cd ..
+
+echo Pushing the audio libraries
+adb push audio/libaudioflinger_inferno.so /system/lib/
+adb push audio/libinfernoaudio.so /system/lib
+
+echo Pushing the custom mediaserver
+adb push audio/mediaserver-inferno /system/bin
+
+adb shell reboot
+

File android-tools/audio/AudioFlinger.cpp

+/* //device/include/server/AudioFlinger/AudioFlinger.cpp
+**
+** Copyright 2007, 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.
+*/
+
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+
+#include <math.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+
+#include <cutils/properties.h>
+
+#include <media/AudioTrack.h>
+#include <media/AudioRecord.h>
+
+#include <private/media/AudioTrackShared.h>
+#include <private/media/AudioEffectShared.h>
+#include <hardware_legacy/AudioHardwareInterface.h>
+
+#include "AudioMixer.h"
+#include "AudioFlinger.h"
+
+#ifdef WITH_A2DP
+#include "A2dpAudioInterface.h"
+#endif
+
+#ifdef LVMX
+#include "lifevibes.h"
+#endif
+
+#include <media/EffectsFactoryApi.h>
+#include <media/EffectVisualizerApi.h>
+
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+// ----------------------------------------------------------------------------
+
+extern const char * const gEffectLibPath;
+
+namespace android {
+
+static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n";
+static const char* kHardwareLockedString = "Hardware lock is taken\n";
+
+//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
+static const float MAX_GAIN = 4096.0f;
+static const float MAX_GAIN_INT = 0x1000;
+
+// retry counts for buffer fill timeout
+// 50 * ~20msecs = 1 second
+static const int8_t kMaxTrackRetries = 50;
+static const int8_t kMaxTrackStartupRetries = 50;
+// allow less retry attempts on direct output thread.
+// direct outputs can be a scarce resource in audio hardware and should
+// be released as quickly as possible.
+static const int8_t kMaxTrackRetriesDirect = 2;
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
+static const nsecs_t kWarningThrottle = seconds(5);
+
+
+#define AUDIOFLINGER_SECURITY_ENABLED 0
+
+// ----------------------------------------------------------------------------
+
+static bool recordingAllowed() {
+#ifndef HAVE_ANDROID_OS
+    return true;
+#endif
+#if AUDIOFLINGER_SECURITY_ENABLED
+    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+    bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO"));
+    if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO");
+    return ok;
+#else
+    return true;
+#endif
+}
+
+static bool settingsAllowed() {
+#ifndef HAVE_ANDROID_OS
+    return true;
+#endif
+#if AUDIOFLINGER_SECURITY_ENABLED
+    if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+    bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
+    if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
+    return ok;
+#else
+    return true;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::AudioFlinger()
+    : BnAudioFlinger(),
+        mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1)
+{
+    mHardwareStatus = AUDIO_HW_IDLE;
+
+    mAudioHardware = AudioHardwareInterface::create();
+
+    mHardwareStatus = AUDIO_HW_INIT;
+    if (mAudioHardware->initCheck() == NO_ERROR) {
+        // open 16-bit output stream for s/w mixer
+        mMode = AudioSystem::MODE_NORMAL;
+        setMode(mMode);
+
+        setMasterVolume(1.0f);
+        setMasterMute(false);
+    } else {
+        LOGE("Couldn't even initialize the stubbed audio hardware!");
+    }
+#ifdef LVMX
+    LifeVibes::init();
+    mLifeVibesClientPid = -1;
+#endif
+}
+
+AudioFlinger::~AudioFlinger()
+{
+    while (!mRecordThreads.isEmpty()) {
+        // closeInput() will remove first entry from mRecordThreads
+        closeInput(mRecordThreads.keyAt(0));
+    }
+    while (!mPlaybackThreads.isEmpty()) {
+        // closeOutput() will remove first entry from mPlaybackThreads
+        closeOutput(mPlaybackThreads.keyAt(0));
+    }
+    if (mAudioHardware) {
+        delete mAudioHardware;
+    }
+}
+
+
+
+status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    result.append("Clients:\n");
+    for (size_t i = 0; i < mClients.size(); ++i) {
+        wp<Client> wClient = mClients.valueAt(i);
+        if (wClient != 0) {
+            sp<Client> client = wClient.promote();
+            if (client != 0) {
+                snprintf(buffer, SIZE, "  pid: %d\n", client->pid());
+                result.append(buffer);
+            }
+        }
+    }
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+
+status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    int hardwareStatus = mHardwareStatus;
+
+    snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    snprintf(buffer, SIZE, "Permission Denial: "
+            "can't dump AudioFlinger from pid=%d, uid=%d\n",
+            IPCThreadState::self()->getCallingPid(),
+            IPCThreadState::self()->getCallingUid());
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+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 AudioFlinger::dump(int fd, const Vector<String16>& args)
+{
+    if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+        dumpPermissionDenial(fd, args);
+    } else {
+        // get state of hardware lock
+        bool hardwareLocked = tryLock(mHardwareLock);
+        if (!hardwareLocked) {
+            String8 result(kHardwareLockedString);
+            write(fd, result.string(), result.size());
+        } else {
+            mHardwareLock.unlock();
+        }
+
+        bool locked = tryLock(mLock);
+
+        // failed to lock - AudioFlinger is probably deadlocked
+        if (!locked) {
+            String8 result(kDeadlockedString);
+            write(fd, result.string(), result.size());
+        }
+
+        dumpClients(fd, args);
+        dumpInternals(fd, args);
+
+        // dump playback threads
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+            mPlaybackThreads.valueAt(i)->dump(fd, args);
+        }
+
+        // dump record threads
+        for (size_t i = 0; i < mRecordThreads.size(); i++) {
+            mRecordThreads.valueAt(i)->dump(fd, args);
+        }
+
+        if (mAudioHardware) {
+            mAudioHardware->dumpState(fd, args);
+        }
+        if (locked) mLock.unlock();
+    }
+    return NO_ERROR;
+}
+
+
+// IAudioFlinger interface
+
+
+sp<IAudioTrack> AudioFlinger::createTrack(
+        pid_t pid,
+        int streamType,
+        uint32_t sampleRate,
+        int format,
+        int channelCount,
+        int frameCount,
+        uint32_t flags,
+        const sp<IMemory>& sharedBuffer,
+        int output,
+        int *sessionId,
+        status_t *status)
+{
+    sp<PlaybackThread::Track> track;
+    sp<TrackHandle> trackHandle;
+    sp<Client> client;
+    wp<Client> wclient;
+    status_t lStatus;
+    int lSessionId;
+
+    if (streamType >= AudioSystem::NUM_STREAM_TYPES) {
+        LOGE("invalid stream type");
+        lStatus = BAD_VALUE;
+        goto Exit;
+    }
+
+    {
+        Mutex::Autolock _l(mLock);
+        PlaybackThread *thread = checkPlaybackThread_l(output);
+        PlaybackThread *effectThread = NULL;
+        if (thread == NULL) {
+            LOGE("unknown output thread");
+            lStatus = BAD_VALUE;
+            goto Exit;
+        }
+
+        wclient = mClients.valueFor(pid);
+
+        if (wclient != NULL) {
+            client = wclient.promote();
+        } else {
+            client = new Client(this, pid);
+            mClients.add(pid, client);
+        }
+
+        LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
+        if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) {
+            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+                sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+                if (mPlaybackThreads.keyAt(i) != output) {
+                    // prevent same audio session on different output threads
+                    uint32_t sessions = t->hasAudioSession(*sessionId);
+                    if (sessions & PlaybackThread::TRACK_SESSION) {
+                        lStatus = BAD_VALUE;
+                        goto Exit;
+                    }
+                    // check if an effect with same session ID is waiting for a track to be created
+                    if (sessions & PlaybackThread::EFFECT_SESSION) {
+                        effectThread = t.get();
+                    }
+                }
+            }
+            lSessionId = *sessionId;
+        } else {
+            // if no audio session id is provided, create one here
+            lSessionId = nextUniqueId();
+            if (sessionId != NULL) {
+                *sessionId = lSessionId;
+            }
+        }
+        LOGV("createTrack() lSessionId: %d", lSessionId);
+
+        track = thread->createTrack_l(client, streamType, sampleRate, format,
+                channelCount, frameCount, sharedBuffer, lSessionId, &lStatus);
+
+        // move effect chain to this output thread if an effect on same session was waiting
+        // for a track to be created
+        if (lStatus == NO_ERROR && effectThread != NULL) {
+            Mutex::Autolock _dl(thread->mLock);
+            Mutex::Autolock _sl(effectThread->mLock);
+            moveEffectChain_l(lSessionId, effectThread, thread, true);
+        }
+    }
+    if (lStatus == NO_ERROR) {
+        trackHandle = new TrackHandle(track);
+    } else {
+        // remove local strong reference to Client before deleting the Track so that the Client
+        // destructor is called by the TrackBase destructor with mLock held
+        client.clear();
+        track.clear();
+    }
+
+Exit:
+    if(status) {
+        *status = lStatus;
+    }
+    return trackHandle;
+}
+
+uint32_t AudioFlinger::sampleRate(int output) const
+{
+    Mutex::Autolock _l(mLock);
+    PlaybackThread *thread = checkPlaybackThread_l(output);
+    if (thread == NULL) {
+        LOGW("sampleRate() unknown thread %d", output);
+        return 0;
+    }
+    return thread->sampleRate();
+}
+
+int AudioFlinger::channelCount(int output) const
+{
+    Mutex::Autolock _l(mLock);
+    PlaybackThread *thread = checkPlaybackThread_l(output);
+    if (thread == NULL) {
+        LOGW("channelCount() unknown thread %d", output);
+        return 0;
+    }
+    return thread->channelCount();
+}
+
+int AudioFlinger::format(int output) const
+{
+    Mutex::Autolock _l(mLock);
+    PlaybackThread *thread = checkPlaybackThread_l(output);
+    if (thread == NULL) {
+        LOGW("format() unknown thread %d", output);
+        return 0;
+    }
+    return thread->format();
+}
+
+size_t AudioFlinger::frameCount(int output) const
+{
+    Mutex::Autolock _l(mLock);
+    PlaybackThread *thread = checkPlaybackThread_l(output);
+    if (thread == NULL) {
+        LOGW("frameCount() unknown thread %d", output);
+        return 0;
+    }
+    return thread->frameCount();
+}
+
+uint32_t AudioFlinger::latency(int output) const
+{
+    Mutex::Autolock _l(mLock);
+    PlaybackThread *thread = checkPlaybackThread_l(output);
+    if (thread == NULL) {
+        LOGW("latency() unknown thread %d", output);
+        return 0;
+    }
+    return thread->latency();
+}
+
+status_t AudioFlinger::setMasterVolume(float value)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    // when hw supports master volume, don't scale in sw mixer
+    AutoMutex lock(mHardwareLock);
+    mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
+    if (mAudioHardware->setMasterVolume(value) == NO_ERROR) {
+        value = 1.0f;
+    }
+    mHardwareStatus = AUDIO_HW_IDLE;
+
+    mMasterVolume = value;
+    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+       mPlaybackThreads.valueAt(i)->setMasterVolume(value);
+
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::setMode(int mode)
+{
+    status_t ret;
+
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+    if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) {
+        LOGW("Illegal value: setMode(%d)", mode);
+        return BAD_VALUE;
+    }
+
+    { // scope for the lock
+        AutoMutex lock(mHardwareLock);
+        mHardwareStatus = AUDIO_HW_SET_MODE;
+        ret = mAudioHardware->setMode(mode);
+        mHardwareStatus = AUDIO_HW_IDLE;
+    }
+
+    if (NO_ERROR == ret) {
+        Mutex::Autolock _l(mLock);
+        mMode = mode;
+        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+           mPlaybackThreads.valueAt(i)->setMode(mode);
+#ifdef LVMX
+        LifeVibes::setMode(mode);
+#endif
+    }
+
+    return ret;
+}
+
+status_t AudioFlinger::setMicMute(bool state)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    AutoMutex lock(mHardwareLock);
+    mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
+    status_t ret = mAudioHardware->setMicMute(state);
+    mHardwareStatus = AUDIO_HW_IDLE;
+    return ret;
+}
+
+bool AudioFlinger::getMicMute() const
+{
+    bool state = AudioSystem::MODE_INVALID;
+    mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
+    mAudioHardware->getMicMute(&state);
+    mHardwareStatus = AUDIO_HW_IDLE;
+    return state;
+}
+
+status_t AudioFlinger::setMasterMute(bool muted)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    mMasterMute = muted;
+    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+       mPlaybackThreads.valueAt(i)->setMasterMute(muted);
+
+    return NO_ERROR;
+}
+
+float AudioFlinger::masterVolume() const
+{
+    return mMasterVolume;
+}
+
+bool AudioFlinger::masterMute() const
+{
+    return mMasterMute;
+}
+
+status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+        return BAD_VALUE;
+    }
+
+    AutoMutex lock(mLock);
+    PlaybackThread *thread = NULL;
+    if (output) {
+        thread = checkPlaybackThread_l(output);
+        if (thread == NULL) {
+            return BAD_VALUE;
+        }
+    }
+
+    mStreamTypes[stream].volume = value;
+
+    if (thread == NULL) {
+        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+           mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
+        }
+    } else {
+        thread->setStreamVolume(stream, value);
+    }
+
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::setStreamMute(int stream, bool muted)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
+        uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
+        return BAD_VALUE;
+    }
+
+    mStreamTypes[stream].mute = muted;
+    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+       mPlaybackThreads.valueAt(i)->setStreamMute(stream, muted);
+
+    return NO_ERROR;
+}
+
+float AudioFlinger::streamVolume(int stream, int output) const
+{
+    if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+        return 0.0f;
+    }
+
+    AutoMutex lock(mLock);
+    float volume;
+    if (output) {
+        PlaybackThread *thread = checkPlaybackThread_l(output);
+        if (thread == NULL) {
+            return 0.0f;
+        }
+        volume = thread->streamVolume(stream);
+    } else {
+        volume = mStreamTypes[stream].volume;
+    }
+
+    return volume;
+}
+
+bool AudioFlinger::streamMute(int stream) const
+{
+    if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) {
+        return true;
+    }
+
+    return mStreamTypes[stream].mute;
+}
+
+bool AudioFlinger::isStreamActive(int stream) const
+{
+    Mutex::Autolock _l(mLock);
+    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+        if (mPlaybackThreads.valueAt(i)->isStreamActive(stream)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
+{
+    status_t result;
+
+    LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d",
+            ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+#ifdef LVMX
+    AudioParameter param = AudioParameter(keyValuePairs);
+    LifeVibes::setParameters(ioHandle,keyValuePairs);
+    String8 key = String8(AudioParameter::keyRouting);
+    int device;
+    if (NO_ERROR != param.getInt(key, device)) {
+        device = -1;
+    }
+
+    key = String8(LifevibesTag);
+    String8 value;
+    int musicEnabled = -1;
+    if (NO_ERROR == param.get(key, value)) {
+        if (value == LifevibesEnable) {
+            mLifeVibesClientPid = IPCThreadState::self()->getCallingPid();
+            musicEnabled = 1;
+        } else if (value == LifevibesDisable) {
+            mLifeVibesClientPid = -1;
+            musicEnabled = 0;
+        }
+    }
+#endif
+
+    // ioHandle == 0 means the parameters are global to the audio hardware interface
+    if (ioHandle == 0) {
+        AutoMutex lock(mHardwareLock);
+        mHardwareStatus = AUDIO_SET_PARAMETER;
+        result = mAudioHardware->setParameters(keyValuePairs);
+#ifdef LVMX
+        if (musicEnabled != -1) {
+            LifeVibes::enableMusic((bool) musicEnabled);
+        }
+#endif
+        mHardwareStatus = AUDIO_HW_IDLE;
+        return result;
+    }
+
+    // hold a strong ref on thread in case closeOutput() or closeInput() is called
+    // and the thread is exited once the lock is released
+    sp<ThreadBase> thread;
+    {
+        Mutex::Autolock _l(mLock);
+        thread = checkPlaybackThread_l(ioHandle);
+        if (thread == NULL) {
+            thread = checkRecordThread_l(ioHandle);
+        }
+    }
+    if (thread != NULL) {
+        result = thread->setParameters(keyValuePairs);
+#ifdef LVMX
+        if ((NO_ERROR == result) && (device != -1)) {
+            LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device);
+        }
+#endif
+        return result;
+    }
+    return BAD_VALUE;
+}
+
+String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
+{
+//    LOGV("getParameters() io %d, keys %s, tid %d, calling tid %d",
+//            ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
+
+    if (ioHandle == 0) {
+        return mAudioHardware->getParameters(keys);
+    }
+
+    Mutex::Autolock _l(mLock);
+
+    PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle);
+    if (playbackThread != NULL) {
+        return playbackThread->getParameters(keys);
+    }
+    RecordThread *recordThread = checkRecordThread_l(ioHandle);
+    if (recordThread != NULL) {
+        return recordThread->getParameters(keys);
+    }
+    return String8("");
+}
+
+size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+    return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount);
+}
+
+unsigned int AudioFlinger::getInputFramesLost(int ioHandle)
+{
+    if (ioHandle == 0) {
+        return 0;
+    }
+
+    Mutex::Autolock _l(mLock);
+
+    RecordThread *recordThread = checkRecordThread_l(ioHandle);
+    if (recordThread != NULL) {
+        return recordThread->getInputFramesLost();
+    }
+    return 0;
+}
+
+status_t AudioFlinger::setVoiceVolume(float value)
+{
+    // check calling permissions
+    if (!settingsAllowed()) {
+        return PERMISSION_DENIED;
+    }
+
+    AutoMutex lock(mHardwareLock);
+    mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
+    status_t ret = mAudioHardware->setVoiceVolume(value);
+    mHardwareStatus = AUDIO_HW_IDLE;
+
+    return ret;
+}
+
+status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output)
+{
+    status_t status;
+
+    Mutex::Autolock _l(mLock);
+
+    PlaybackThread *playbackThread = checkPlaybackThread_l(output);
+    if (playbackThread != NULL) {
+        return playbackThread->getRenderPosition(halFrames, dspFrames);
+    }
+
+    return BAD_VALUE;
+}
+
+void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
+{
+
+    Mutex::Autolock _l(mLock);
+
+    int pid = IPCThreadState::self()->getCallingPid();
+    if (mNotificationClients.indexOfKey(pid) < 0) {
+        sp<NotificationClient> notificationClient = new NotificationClient(this,
+                                                                            client,
+                                                                            pid);
+        LOGV("registerClient() client %p, pid %d", notificationClient.get(), pid);
+
+        mNotificationClients.add(pid, notificationClient);
+
+        sp<IBinder> binder = client->asBinder();
+        binder->linkToDeath(notificationClient);
+
+        // the config change is always sent from playback or record threads to avoid deadlock
+        // with AudioSystem::gLock
+        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+            mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+        }
+
+        for (size_t i = 0; i < mRecordThreads.size(); i++) {
+            mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
+        }
+    }
+}
+
+void AudioFlinger::removeNotificationClient(pid_t pid)
+{
+    Mutex::Autolock _l(mLock);
+
+    int index = mNotificationClients.indexOfKey(pid);
+    if (index >= 0) {
+        sp <NotificationClient> client = mNotificationClients.valueFor(pid);
+        LOGV("removeNotificationClient() %p, pid %d", client.get(), pid);
+#ifdef LVMX
+        if (pid == mLifeVibesClientPid) {
+            LOGV("Disabling lifevibes");
+            LifeVibes::enableMusic(false);
+            mLifeVibesClientPid = -1;
+        }
+#endif
+        mNotificationClients.removeItem(pid);
+    }
+}
+
+// audioConfigChanged_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2)
+{
+    size_t size = mNotificationClients.size();
+    for (size_t i = 0; i < size; i++) {
+        mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2);
+    }
+}
+
+// removeClient_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::removeClient_l(pid_t pid)
+{
+    LOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
+    mClients.removeItem(pid);
+}
+
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id)
+    :   Thread(false),
+        mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
+        mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false)
+{
+}
+
+AudioFlinger::ThreadBase::~ThreadBase()
+{
+    mParamCond.broadcast();
+    mNewParameters.clear();
+}
+
+void AudioFlinger::ThreadBase::exit()
+{
+    // keep a strong ref on ourself so that we wont get
+    // destroyed in the middle of requestExitAndWait()
+    sp <ThreadBase> strongMe = this;
+
+    LOGV("ThreadBase::exit");
+    {
+        AutoMutex lock(&mLock);
+        mExiting = true;
+        requestExit();
+        mWaitWorkCV.signal();
+    }
+    requestExitAndWait();
+}
+
+uint32_t AudioFlinger::ThreadBase::sampleRate() const
+{
+    return mSampleRate;
+}
+
+int AudioFlinger::ThreadBase::channelCount() const
+{
+    return (int)mChannelCount;
+}
+
+int AudioFlinger::ThreadBase::format() const
+{
+    return mFormat;
+}
+
+size_t AudioFlinger::ThreadBase::frameCount() const
+{
+    return mFrameCount;
+}
+
+status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
+{
+    status_t status;
+
+    LOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
+    Mutex::Autolock _l(mLock);
+
+    mNewParameters.add(keyValuePairs);
+    mWaitWorkCV.signal();
+    // wait condition with timeout in case the thread loop has exited
+    // before the request could be processed
+    if (mParamCond.waitRelative(mLock, seconds(2)) == NO_ERROR) {
+        status = mParamStatus;
+        mWaitWorkCV.signal();
+    } else {
+        status = TIMED_OUT;
+    }
+    return status;
+}
+
+void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param)
+{
+    Mutex::Autolock _l(mLock);
+    sendConfigEvent_l(event, param);
+}
+
+// sendConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
+{
+    ConfigEvent *configEvent = new ConfigEvent();
+    configEvent->mEvent = event;
+    configEvent->mParam = param;
+    mConfigEvents.add(configEvent);
+    LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
+    mWaitWorkCV.signal();
+}
+
+void AudioFlinger::ThreadBase::processConfigEvents()
+{
+    mLock.lock();
+    while(!mConfigEvents.isEmpty()) {
+        LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
+        ConfigEvent *configEvent = mConfigEvents[0];
+        mConfigEvents.removeAt(0);
+        // release mLock before locking AudioFlinger mLock: lock order is always
+        // AudioFlinger then ThreadBase to avoid cross deadlock
+        mLock.unlock();
+        mAudioFlinger->mLock.lock();
+        audioConfigChanged_l(configEvent->mEvent, configEvent->mParam);
+        mAudioFlinger->mLock.unlock();
+        delete configEvent;
+        mLock.lock();
+    }
+    mLock.unlock();
+}
+
+status_t AudioFlinger::ThreadBase::dumpBase(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, "thread %p maybe dead locked\n", this);
+        write(fd, buffer, strlen(buffer));
+    }
+
+    snprintf(buffer, SIZE, "standby: %d\n", mStandby);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "Frame count: %d\n", mFrameCount);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "Format: %d\n", mFormat);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize);
+    result.append(buffer);
+
+    snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
+    result.append(buffer);
+    result.append(" Index Command");
+    for (size_t i = 0; i < mNewParameters.size(); ++i) {
+        snprintf(buffer, SIZE, "\n %02d    ", i);
+        result.append(buffer);
+        result.append(mNewParameters[i]);
+    }
+
+    snprintf(buffer, SIZE, "\n\nPending config events: \n");
+    result.append(buffer);
+    snprintf(buffer, SIZE, " Index event param\n");
+    result.append(buffer);
+    for (size_t i = 0; i < mConfigEvents.size(); i++) {
+        snprintf(buffer, SIZE, " %02d    %02d    %d\n", i, mConfigEvents[i]->mEvent, mConfigEvents[i]->mParam);
+        result.append(buffer);
+    }
+    result.append("\n");
+
+    write(fd, result.string(), result.size());
+
+    if (locked) {
+        mLock.unlock();
+    }
+    return NO_ERROR;
+}
+
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+    :   ThreadBase(audioFlinger, id),
+        mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
+        mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
+        mDevice(device)
+{
+    readOutputParameters();
+
+    mMasterVolume = mAudioFlinger->masterVolume();
+    mMasterMute = mAudioFlinger->masterMute();
+
+    for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+        mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
+        mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+    }
+}
+
+AudioFlinger::PlaybackThread::~PlaybackThread()
+{
+    delete [] mMixBuffer;
+}
+
+status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
+{
+    dumpInternals(fd, args);
+    dumpTracks(fd, args);
+    dumpEffectChains(fd, args);
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
+    result.append(buffer);
+    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        sp<Track> track = mTracks[i];
+        if (track != 0) {
+            track->dump(buffer, SIZE);
+            result.append(buffer);
+        }
+    }
+
+    snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
+    result.append(buffer);
+    result.append("   Name  Clien Typ Fmt Chn Session Buf  S M F SRate LeftV RighV  Serv       User       Main buf   Aux Buf\n");
+    for (size_t i = 0; i < mActiveTracks.size(); ++i) {
+        wp<Track> wTrack = mActiveTracks[i];
+        if (wTrack != 0) {
+            sp<Track> track = wTrack.promote();
+            if (track != 0) {
+                track->dump(buffer, SIZE);
+                result.append(buffer);
+            }
+        }
+    }
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size());
+    write(fd, buffer, strlen(buffer));
+
+    for (size_t i = 0; i < mEffectChains.size(); ++i) {
+        sp<EffectChain> chain = mEffectChains[i];
+        if (chain != 0) {
+            chain->dump(fd, args);
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
+    result.append(buffer);
+    snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    dumpBase(fd, args);
+
+    return NO_ERROR;
+}
+
+// Thread virtuals
+status_t AudioFlinger::PlaybackThread::readyToRun()
+{
+    if (mSampleRate == 0) {
+        LOGE("No working audio driver found.");
+        return NO_INIT;
+    }
+    LOGI("AudioFlinger's thread %p ready to run", this);
+    return NO_ERROR;
+}
+
+void AudioFlinger::PlaybackThread::onFirstRef()
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+
+    snprintf(buffer, SIZE, "Playback Thread %p", this);
+
+    run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::PlaybackThread::Track>  AudioFlinger::PlaybackThread::createTrack_l(
+        const sp<AudioFlinger::Client>& client,
+        int streamType,
+        uint32_t sampleRate,
+        int format,
+        int channelCount,
+        int frameCount,
+        const sp<IMemory>& sharedBuffer,
+        int sessionId,
+        status_t *status)
+{
+    sp<Track> track;
+    status_t lStatus;
+
+    if (mType == DIRECT) {
+        if (sampleRate != mSampleRate || format != mFormat || channelCount != (int)mChannelCount) {
+            LOGE("createTrack_l() Bad parameter:  sampleRate %d format %d, channelCount %d for output %p",
+                 sampleRate, format, channelCount, mOutput);
+            lStatus = BAD_VALUE;
+            goto Exit;
+        }
+    } else {
+        // Resampler implementation limits input sampling rate to 2 x output sampling rate.
+        if (sampleRate > mSampleRate*2) {
+            LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
+            lStatus = BAD_VALUE;
+            goto Exit;
+        }
+    }
+
+    if (mOutput == 0) {
+        LOGE("Audio driver not initialized.");
+        lStatus = NO_INIT;
+        goto Exit;
+    }
+
+    { // scope for mLock
+        Mutex::Autolock _l(mLock);
+
+        // all tracks in same audio session must share the same routing strategy otherwise
+        // conflicts will happen when tracks are moved from one output to another by audio policy
+        // manager
+        uint32_t strategy =
+                AudioSystem::getStrategyForStream((AudioSystem::stream_type)streamType);
+        for (size_t i = 0; i < mTracks.size(); ++i) {
+            sp<Track> t = mTracks[i];
+            if (t != 0) {
+                if (sessionId == t->sessionId() &&
+                        strategy != AudioSystem::getStrategyForStream((AudioSystem::stream_type)t->type())) {
+                    lStatus = BAD_VALUE;
+                    goto Exit;
+                }
+            }
+        }
+
+        track = new Track(this, client, streamType, sampleRate, format,
+                channelCount, frameCount, sharedBuffer, sessionId);
+        if (track->getCblk() == NULL || track->name() < 0) {
+            lStatus = NO_MEMORY;
+            goto Exit;
+        }
+        mTracks.add(track);
+
+        sp<EffectChain> chain = getEffectChain_l(sessionId);
+        if (chain != 0) {
+            LOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
+            track->setMainBuffer(chain->inBuffer());
+            chain->setStrategy(AudioSystem::getStrategyForStream((AudioSystem::stream_type)track->type()));
+        }
+    }
+    lStatus = NO_ERROR;
+
+Exit:
+    if(status) {
+        *status = lStatus;
+    }
+    return track;
+}
+
+uint32_t AudioFlinger::PlaybackThread::latency() const
+{
+    if (mOutput) {
+        return mOutput->latency();
+    }
+    else {
+        return 0;
+    }
+}
+
+status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
+{
+#ifdef LVMX
+    int audioOutputType = LifeVibes::getMixerType(mId, mType);
+    if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+        LifeVibes::setMasterVolume(audioOutputType, value);
+    }
+#endif
+    mMasterVolume = value;
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
+{
+#ifdef LVMX
+    int audioOutputType = LifeVibes::getMixerType(mId, mType);
+    if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+        LifeVibes::setMasterMute(audioOutputType, muted);
+    }
+#endif
+    mMasterMute = muted;
+    return NO_ERROR;
+}
+
+float AudioFlinger::PlaybackThread::masterVolume() const
+{
+    return mMasterVolume;
+}
+
+bool AudioFlinger::PlaybackThread::masterMute() const
+{
+    return mMasterMute;
+}
+
+status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
+{
+#ifdef LVMX
+    int audioOutputType = LifeVibes::getMixerType(mId, mType);
+    if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+        LifeVibes::setStreamVolume(audioOutputType, stream, value);
+    }
+#endif
+    mStreamTypes[stream].volume = value;
+    return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted)
+{
+#ifdef LVMX
+    int audioOutputType = LifeVibes::getMixerType(mId, mType);
+    if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+        LifeVibes::setStreamMute(audioOutputType, stream, muted);
+    }
+#endif
+    mStreamTypes[stream].mute = muted;
+    return NO_ERROR;
+}
+
+float AudioFlinger::PlaybackThread::streamVolume(int stream) const
+{
+    return mStreamTypes[stream].volume;
+}
+
+bool AudioFlinger::PlaybackThread::streamMute(int stream) const
+{
+    return mStreamTypes[stream].mute;
+}
+
+bool AudioFlinger::PlaybackThread::isStreamActive(int stream) const
+{
+    Mutex::Autolock _l(mLock);
+    size_t count = mActiveTracks.size();
+    for (size_t i = 0 ; i < count ; ++i) {
+        sp<Track> t = mActiveTracks[i].promote();
+        if (t == 0) continue;
+        Track* const track = t.get();
+        if (t->type() == stream)
+            return true;
+    }
+    return false;
+}
+
+// addTrack_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
+{
+    status_t status = ALREADY_EXISTS;
+
+    // set retry count for buffer fill
+    track->mRetryCount = kMaxTrackStartupRetries;
+    if (mActiveTracks.indexOf(track) < 0) {
+        // the track is newly added, make sure it fills up all its
+        // buffers before playing. This is to ensure the client will
+        // effectively get the latency it requested.
+        track->mFillingUpStatus = Track::FS_FILLING;
+        track->mResetDone = false;
+        mActiveTracks.add(track);
+        if (track->mainBuffer() != mMixBuffer) {
+            sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+            if (chain != 0) {
+                LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
+                chain->startTrack();
+            }
+        }
+
+        status = NO_ERROR;
+    }
+
+    LOGV("mWaitWorkCV.broadcast");
+    mWaitWorkCV.broadcast();
+
+    return status;
+}
+
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
+{
+    track->mState = TrackBase::TERMINATED;
+    if (mActiveTracks.indexOf(track) < 0) {
+        mTracks.remove(track);
+        deleteTrackName_l(track->name());
+    }
+}
+
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
+    return mOutput->getParameters(keys);
+}
+
+// destroyTrack_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
+    AudioSystem::OutputDescriptor desc;
+    void *param2 = 0;
+
+    LOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
+
+    switch (event) {
+    case AudioSystem::OUTPUT_OPENED:
+    case AudioSystem::OUTPUT_CONFIG_CHANGED:
+        desc.channels = mChannels;
+        desc.samplingRate = mSampleRate;
+        desc.format = mFormat;
+        desc.frameCount = mFrameCount;
+        desc.latency = latency();
+        param2 = &desc;
+        break;
+
+    case AudioSystem::STREAM_CONFIG_CHANGED:
+        param2 = &param;
+    case AudioSystem::OUTPUT_CLOSED:
+    default:
+        break;
+    }
+    mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+}
+
+void AudioFlinger::PlaybackThread::readOutputParameters()
+{
+    mSampleRate = mOutput->sampleRate();
+    mChannels = mOutput->channels();
+    mChannelCount = (uint16_t)AudioSystem::popCount(mChannels);
+    mFormat = mOutput->format();
+    mFrameSize = (uint16_t)mOutput->frameSize();
+    mFrameCount = mOutput->bufferSize() / mFrameSize;
+
+    // FIXME - Current mixer implementation only supports stereo output: Always
+    // Allocate a stereo buffer even if HW output is mono.
+    if (mMixBuffer != NULL) delete[] mMixBuffer;
+    mMixBuffer = new int16_t[mFrameCount * 2];
+    memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+
+    // force reconfiguration of effect chains and engines to take new buffer size and audio
+    // parameters into account
+    // Note that mLock is not held when readOutputParameters() is called from the constructor
+    // but in this case nothing is done below as no audio sessions have effect yet so it doesn't
+    // matter.
+    // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
+    Vector< sp<EffectChain> > effectChains = mEffectChains;
+    for (size_t i = 0; i < effectChains.size(); i ++) {
+        mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
+    }
+}
+
+status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
+{
+    if (halFrames == 0 || dspFrames == 0) {
+        return BAD_VALUE;
+    }
+    if (mOutput == 0) {
+        return INVALID_OPERATION;
+    }
+    *halFrames = mBytesWritten/mOutput->frameSize();
+
+    return mOutput->getRenderPosition(dspFrames);
+}
+
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId)
+{
+    Mutex::Autolock _l(mLock);
+    uint32_t result = 0;
+    if (getEffectChain_l(sessionId) != 0) {
+        result = EFFECT_SESSION;
+    }
+
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        sp<Track> track = mTracks[i];
+        if (sessionId == track->sessionId() &&
+                !(track->mCblk->flags & CBLK_INVALID_MSK)) {
+            result |= TRACK_SESSION;
+            break;
+        }
+    }
+
+    return result;
+}
+
+uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
+{
+    // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+    // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
+    if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) {
+        return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+    }
+    for (size_t i = 0; i < mTracks.size(); i++) {
+        sp<Track> track = mTracks[i];
+        if (sessionId == track->sessionId() &&
+                !(track->mCblk->flags & CBLK_INVALID_MSK)) {
+            return AudioSystem::getStrategyForStream((AudioSystem::stream_type) track->type());
+        }
+    }
+    return AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId)
+{
+    Mutex::Autolock _l(mLock);
+    return getEffectChain_l(sessionId);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId)
+{
+    sp<EffectChain> chain;
+
+    size_t size = mEffectChains.size();
+    for (size_t i = 0; i < size; i++) {
+        if (mEffectChains[i]->sessionId() == sessionId) {
+            chain = mEffectChains[i];
+            break;
+        }
+    }
+    return chain;
+}
+
+void AudioFlinger::PlaybackThread::setMode(uint32_t mode)
+{
+    Mutex::Autolock _l(mLock);
+    size_t size = mEffectChains.size();
+    for (size_t i = 0; i < size; i++) {
+        mEffectChains[i]->setMode_l(mode);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
+    :   PlaybackThread(audioFlinger, output, id, device),
+        mAudioMixer(0)
+{
+    mType = PlaybackThread::MIXER;
+    mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+
+    // FIXME - Current mixer implementation only supports stereo output
+    if (mChannelCount == 1) {
+        LOGE("Invalid audio hardware channel count");
+    }
+}
+
+AudioFlinger::MixerThread::~MixerThread()
+{
+    delete mAudioMixer;
+}
+
+bool AudioFlinger::MixerThread::threadLoop()
+{
+    Vector< sp<Track> > tracksToRemove;
+    uint32_t mixerStatus = MIXER_IDLE;
+    nsecs_t standbyTime = systemTime();
+    size_t mixBufferSize = mFrameCount * mFrameSize;
+    // FIXME: Relaxed timing because of a certain device that can't meet latency
+    // Should be reduced to 2x after the vendor fixes the driver issue
+    nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
+    nsecs_t lastWarning = 0;
+    bool longStandbyExit = false;
+    uint32_t activeSleepTime = activeSleepTimeUs();
+    uint32_t idleSleepTime = idleSleepTimeUs();
+    uint32_t sleepTime = idleSleepTime;
+    Vector< sp<EffectChain> > effectChains;
+
+    while (!exitPending())
+    {
+        processConfigEvents();
+
+        mixerStatus = MIXER_IDLE;
+        { // scope for mLock
+
+            Mutex::Autolock _l(mLock);
+
+            if (checkForNewParameters_l()) {
+                mixBufferSize = mFrameCount * mFrameSize;
+                // FIXME: Relaxed timing because of a certain device that can't meet latency
+                // Should be reduced to 2x after the vendor fixes the driver issue
+                maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
+                activeSleepTime = activeSleepTimeUs();
+                idleSleepTime = idleSleepTimeUs();
+            }
+
+            const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
+
+            // put audio hardware into standby after short delay
+            if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+                        mSuspended) {
+                if (!mStandby) {
+                    LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
+                    mOutput->standby();
+                    mStandby = true;
+                    mBytesWritten = 0;
+                }
+
+                if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+                    // we're about to wait, flush the binder command buffer
+                    IPCThreadState::self()->flushCommands();
+
+                    if (exitPending()) break;
+
+                    // wait until we have something to do...
+                    LOGV("MixerThread %p TID %d going to sleep\n", this, gettid());
+                    mWaitWorkCV.wait(mLock);
+                    LOGV("MixerThread %p TID %d waking up\n", this, gettid());
+
+                    if (mMasterMute == false) {
+                        char value[PROPERTY_VALUE_MAX];
+                        property_get("ro.audio.silent", value, "0");
+                        if (atoi(value)) {
+                            LOGD("Silence is golden");
+                            setMasterMute(true);
+                        }
+                    }
+
+                    standbyTime = systemTime() + kStandbyTimeInNsecs;
+                    sleepTime = idleSleepTime;
+                    continue;
+                }
+            }
+
+            mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
+
+            // prevent any changes in effect chain list and in each effect chain
+            // during mixing and effect process as the audio buffers could be deleted
+            // or modified if an effect is created or deleted
+            lockEffectChains_l(effectChains);
+       }
+
+        if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
+            // mix buffers...
+            mAudioMixer->process();
+            sleepTime = 0;
+            standbyTime = systemTime() + kStandbyTimeInNsecs;
+            //TODO: delay standby when effects have a tail
+        } else {
+            // If no tracks are ready, sleep once for the duration of an output
+            // buffer size, then write 0s to the output
+            if (sleepTime == 0) {
+                if (mixerStatus == MIXER_TRACKS_ENABLED) {
+                    sleepTime = activeSleepTime;
+                } else {
+                    sleepTime = idleSleepTime;
+                }
+            } else if (mBytesWritten != 0 ||
+                       (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
+                memset (mMixBuffer, 0, mixBufferSize);
+                sleepTime = 0;
+                LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
+            }
+            // TODO add standby time extension fct of effect tail
+        }
+
+        if (mSuspended) {
+            sleepTime = suspendSleepTimeUs();
+        }
+        // sleepTime == 0 means we must write to audio hardware
+        if (sleepTime == 0) {
+             for (size_t i = 0; i < effectChains.size(); i ++) {
+                 effectChains[i]->process_l();
+             }
+             // enable changes in effect chain
+             unlockEffectChains(effectChains);
+#ifdef LVMX
+            int audioOutputType = LifeVibes::getMixerType(mId, mType);
+            if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) {
+               LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize);
+            }
+#endif
+            mLastWriteTime = systemTime();
+            mInWrite = true;
+            mBytesWritten += mixBufferSize;
+
+            int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+            if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
+            mNumWrites++;
+            mInWrite = false;
+            nsecs_t now = systemTime();
+            nsecs_t delta = now - mLastWriteTime;
+            if (delta > maxPeriod) {
+                mNumDelayedWrites++;
+                if ((now - lastWarning) > kWarningThrottle) {
+                    LOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+                            ns2ms(delta), mNumDelayedWrites, this);
+                    lastWarning = now;
+                }
+                if (mStandby) {
+                    longStandbyExit = true;
+                }
+            }
+            mStandby = false;
+        } else {
+            // enable changes in effect chain
+            unlockEffectChains(effectChains);
+            usleep(sleepTime);
+        }
+
+        // finally let go of all our tracks, without the lock held
+        // since we can't guarantee the destructors won't acquire that
+        // same lock.
+        tracksToRemove.clear();
+
+        // Effect chains will be actually deleted here if they were removed from
+        // mEffectChains list during mixing or effects processing
+        effectChains.clear();
+    }
+
+    if (!mStandby) {
+        mOutput->standby();
+    }
+
+    LOGV("MixerThread %p exiting", this);
+    return false;
+}
+
+// prepareTracks_l() must be called with ThreadBase::mLock held
+uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+{
+
+    uint32_t mixerStatus = MIXER_IDLE;
+    // find out which tracks need to be processed
+    size_t count = activeTracks.size();
+    size_t mixedTracks = 0;
+    size_t tracksWithEffect = 0;
+
+    float masterVolume = mMasterVolume;
+    bool  masterMute = mMasterMute;
+
+    if (masterMute) {
+        masterVolume = 0;
+    }
+#ifdef LVMX
+    bool tracksConnectedChanged = false;
+    bool stateChanged = false;
+
+    int audioOutputType = LifeVibes::getMixerType(mId, mType);
+    if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
+    {
+        int activeTypes = 0;
+        for (size_t i=0 ; i<count ; i++) {
+            sp<Track> t = activeTracks[i].promote();
+            if (t == 0) continue;
+            Track* const track = t.get();
+            int iTracktype=track->type();
+            activeTypes |= 1<<track->type();
+        }
+        LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute);
+    }
+#endif
+    // Delegate master volume control to effect in output mix effect chain if needed
+    sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
+    if (chain != 0) {
+        uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+        chain->setVolume_l(&v, &v);
+        masterVolume = (float)((v + (1 << 23)) >> 24);
+        chain.clear();
+    }
+
+    for (size_t i=0 ; i<count ; i++) {
+        sp<Track> t = activeTracks[i].promote();
+        if (t == 0) continue;
+
+        Track* const track = t.get();
+        audio_track_cblk_t* cblk = track->cblk();
+
+        // The first time a track is added we wait
+        // for all its buffers to be filled before processing it
+        mAudioMixer->setActiveTrack(track->name());
+        if (cblk->framesReady() && track->isReady() &&
+                !track->isPaused() && !track->isTerminated())
+        {
+            //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this);
+
+            mixedTracks++;
+
+            // track->mainBuffer() != mMixBuffer means there is an effect chain
+            // connected to the track
+            chain.clear();
+            if (track->mainBuffer() != mMixBuffer) {
+                chain = getEffectChain_l(track->sessionId());
+                // Delegate volume control to effect in track effect chain if needed
+                if (chain != 0) {
+                    tracksWithEffect++;
+                } else {
+                    LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d",
+                            track->name(), track->sessionId());
+                }
+            }
+
+
+            int param = AudioMixer::VOLUME;
+            if (track->mFillingUpStatus == Track::FS_FILLED) {
+                // no ramp for the first volume setting
+                track->mFillingUpStatus = Track::FS_ACTIVE;
+                if (track->mState == TrackBase::RESUMING) {
+                    track->mState = TrackBase::ACTIVE;
+                    param = AudioMixer::RAMP_VOLUME;
+                }
+            } else if (cblk->server != 0) {
+                // If the track is stopped before the first frame was mixed,
+                // do not apply ramp
+                param = AudioMixer::RAMP_VOLUME;
+            }
+
+            // compute volume for this track
+            uint32_t vl, vr, va;
+            if (track->isMuted() || track->isPausing() ||
+                mStreamTypes[track->type()].mute) {
+                vl = vr = va = 0;
+                if (track->isPausing()) {
+                    track->setPaused();
+                }
+            } else {
+
+                // read original volumes with volume control
+                float typeVolume = mStreamTypes[track->type()].volume;
+#ifdef LVMX
+                bool streamMute=false;
+                // read the volume from the LivesVibes audio engine.
+                if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType))
+                {
+                    LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute);
+                    if (streamMute) {
+                        typeVolume = 0;
+                    }
+                }
+#endif
+                float v = masterVolume * typeVolume;
+                vl = (uint32_t)(v * cblk->volume[0]) << 12;
+                vr = (uint32_t)(v * cblk->volume[1]) << 12;
+
+                va = (uint32_t)(v * cblk->sendLevel);
+            }
+            // Delegate volume control to effect in track effect chain if needed
+            if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
+                // Do not ramp volume if volume is controlled by effect
+                param = AudioMixer::VOLUME;
+                track->mHasVolumeController = true;
+            } else {
+                // force no volume ramp when volume controller was just disabled or removed
+                // from effect chain to avoid volume spike
+                if (track->mHasVolumeController) {
+                    param = AudioMixer::VOLUME;
+                }
+                track->mHasVolumeController = false;
+            }
+
+            // Convert volumes from 8.24 to 4.12 format
+            int16_t left, right, aux;
+            uint32_t v_clamped = (vl + (1 << 11)) >> 12;
+            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+            left = int16_t(v_clamped);
+            v_clamped = (vr + (1 << 11)) >> 12;
+            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
+            right = int16_t(v_clamped);
+
+            if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
+            aux = int16_t(va);
+
+#ifdef LVMX
+            if ( tracksConnectedChanged || stateChanged )
+            {
+                 // only do the ramp when the volume is changed by the user / application
+                 param = AudioMixer::VOLUME;
+            }
+#endif
+
+            // XXX: these things DON'T need to be done each time
+            mAudioMixer->setBufferProvider(track);
+            mAudioMixer->enable(AudioMixer::MIXING);
+
+            mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
+            mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);
+            mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::FORMAT, (void *)track->format());
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::CHANNEL_COUNT, (void *)track->channelCount());
+            mAudioMixer->setParameter(
+                AudioMixer::RESAMPLE,
+                AudioMixer::SAMPLE_RATE,
+                (void *)(cblk->sampleRate));
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+            mAudioMixer->setParameter(
+                AudioMixer::TRACK,
+                AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
+
+            // reset retry count
+            track->mRetryCount = kMaxTrackRetries;
+            mixerStatus = MIXER_TRACKS_READY;
+        } else {
+            //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this);
+            if (track->isStopped()) {
+                track->reset();
+            }
+            if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+                // We have consumed all the buffers of this track.
+                // Remove it from the list of active tracks.
+                tracksToRemove->add(track);
+            } else {
+                // No buffers for this track. Give it a few chances to
+                // fill a buffer, then remove it from active list.
+                if (--(track->mRetryCount) <= 0) {
+                    LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
+                    tracksToRemove->add(track);
+                    // indicate to client process that the track was disabled because of underrun
+                    cblk->flags |= CBLK_DISABLED_ON;
+                } else if (mixerStatus != MIXER_TRACKS_READY) {
+                    mixerStatus = MIXER_TRACKS_ENABLED;
+                }
+            }
+            mAudioMixer->disable(AudioMixer::MIXING);
+        }
+    }
+
+    // remove all the tracks that need to be...
+    count = tracksToRemove->size();
+    if (UNLIKELY(count)) {
+        for (size_t i=0 ; i<count ; i++) {
+            const sp<Track>& track = tracksToRemove->itemAt(i);
+            mActiveTracks.remove(track);
+            if (track->mainBuffer() != mMixBuffer) {
+                chain = getEffectChain_l(track->sessionId());
+                if (chain != 0) {
+                    LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
+                    chain->stopTrack();
+                }
+            }
+            if (track->isTerminated()) {
+                mTracks.remove(track);
+                deleteTrackName_l(track->mName);
+            }
+        }
+    }
+