Merge "Disable verbose logging"
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index a79a9ff..6fe0c7f 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
 
 #include <hardware/audio_effect.h>
 #include <media/IAudioFlingerClient.h>
+#include <media/IAudioPolicyServiceClient.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include <utils/Errors.h>
@@ -301,6 +302,21 @@
 
     // ----------------------------------------------------------------------------
 
+    class AudioPortCallback : public RefBase
+    {
+    public:
+
+                AudioPortCallback() {}
+        virtual ~AudioPortCallback() {}
+
+        virtual void onAudioPortListUpdate() = 0;
+        virtual void onAudioPatchListUpdate() = 0;
+        virtual void onServiceDied() = 0;
+
+    };
+
+    static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+
 private:
 
     class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -319,7 +335,8 @@
         virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
     };
 
-    class AudioPolicyServiceClient: public IBinder::DeathRecipient
+    class AudioPolicyServiceClient: public IBinder::DeathRecipient,
+                                    public BnAudioPolicyServiceClient
     {
     public:
         AudioPolicyServiceClient() {
@@ -327,6 +344,10 @@
 
         // DeathRecipient
         virtual void binderDied(const wp<IBinder>& who);
+
+        // IAudioPolicyServiceClient
+        virtual void onAudioPortListUpdate();
+        virtual void onAudioPatchListUpdate();
     };
 
     static sp<AudioFlingerClient> gAudioFlingerClient;
@@ -349,6 +370,8 @@
     // list of output descriptors containing cached parameters
     // (sampling rate, framecount, channel count...)
     static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+
+    static sp<AudioPortCallback> gAudioPortCallback;
 };
 
 };  // namespace android
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 6b6df6e..d422aa3 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -25,6 +25,7 @@
 #include <utils/Errors.h>
 #include <binder/IInterface.h>
 #include <media/AudioSystem.h>
+#include <media/IAudioPolicyServiceClient.h>
 
 #include <system/audio_policy.h>
 
@@ -124,6 +125,7 @@
     /* Set audio port configuration */
     virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
 
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
 };
 
 
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
new file mode 100644
index 0000000..59df046
--- /dev/null
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyServiceClient : public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(AudioPolicyServiceClient);
+
+    // Notifies a change of audio port configuration.
+    virtual void onAudioPortListUpdate() = 0;
+    // Notifies a change of audio patch configuration.
+    virtual void onAudioPatchListUpdate() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk
index b506353..ee283a6 100644
--- a/media/libcpustats/Android.mk
+++ b/media/libcpustats/Android.mk
@@ -1,4 +1,4 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
@@ -8,4 +8,6 @@
 
 LOCAL_MODULE := libcpustats
 
+LOCAL_CFLAGS := -std=gnu++11 -Werror
+
 include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index 637402a..cfdcb51 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -21,7 +21,6 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include <utils/Debug.h>
 #include <utils/Log.h>
 
 #include <cpustats/ThreadCpuUsage.h>
@@ -218,7 +217,7 @@
 #define FREQ_SIZE 64
             char freq_path[FREQ_SIZE];
 #define FREQ_DIGIT 27
-            COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+            static_assert(MAX_CPU <= 10, "MAX_CPU too large");
 #define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq"
             strlcpy(freq_path, FREQ_PATH, sizeof(freq_path));
             freq_path[FREQ_DIGIT] = cpuNum + '0';
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index f3770e4..69eead3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -44,6 +44,7 @@
     JetPlayer.cpp \
     IOMX.cpp \
     IAudioPolicyService.cpp \
+    IAudioPolicyServiceClient.cpp \
     MediaScanner.cpp \
     MediaScannerClient.cpp \
     CharacterEncodingDetector.cpp \
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 845ee20..eafb3ad 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -45,6 +45,7 @@
 audio_channel_mask_t AudioSystem::gPrevInChannelMask;
 size_t AudioSystem::gInBuffSize = 0;    // zero indicates cache is invalid
 
+sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback;
 
 // establish binder interface to AudioFlinger service
 const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
@@ -528,6 +529,7 @@
     gAudioErrorCallback = cb;
 }
 
+
 bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType)
 {
     switch (streamType) {
@@ -566,6 +568,7 @@
         }
         binder->linkToDeath(gAudioPolicyServiceClient);
         gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
+        gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
         gLock.unlock();
     } else {
         gLock.unlock();
@@ -880,14 +883,39 @@
     return aps->setAudioPortConfig(config);
 }
 
+void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack)
+{
+    Mutex::Autolock _l(gLock);
+    gAudioPortCallback = callBack;
+}
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
 {
-    Mutex::Autolock _l(AudioSystem::gLock);
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onServiceDied();
+    }
     AudioSystem::gAudioPolicyService.clear();
 
     ALOGW("AudioPolicyService server died!");
 }
 
+void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
+{
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onAudioPortListUpdate();
+    }
+}
+
+void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate()
+{
+    Mutex::Autolock _l(gLock);
+    if (gAudioPortCallback != 0) {
+        gAudioPortCallback->onAudioPatchListUpdate();
+    }
+}
+
 }; // namespace android
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index ad2d4eb..eee72c5 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -63,7 +63,8 @@
     CREATE_AUDIO_PATCH,
     RELEASE_AUDIO_PATCH,
     LIST_AUDIO_PATCHES,
-    SET_AUDIO_PORT_CONFIG
+    SET_AUDIO_PORT_CONFIG,
+    REGISTER_CLIENT
 };
 
 class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -524,6 +525,13 @@
         }
         return status;
     }
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.writeStrongBinder(client->asBinder());
+        remote()->transact(REGISTER_CLIENT, data, &reply);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -910,6 +918,13 @@
             reply->writeInt32(status);
             return NO_ERROR;
         }
+        case REGISTER_CLIENT: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
+                    data.readStrongBinder());
+            registerClient(client);
+            return NO_ERROR;
+        } break;
 
         default:
             return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
new file mode 100644
index 0000000..e802277
--- /dev/null
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 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 "IAudioPolicyServiceClient"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IAudioPolicyServiceClient.h>
+#include <media/AudioSystem.h>
+
+namespace android {
+
+enum {
+    PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
+    PATCH_LIST_UPDATE
+};
+
+class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
+{
+public:
+    BpAudioPolicyServiceClient(const sp<IBinder>& impl)
+        : BpInterface<IAudioPolicyServiceClient>(impl)
+    {
+    }
+
+    void onAudioPortListUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+        remote()->transact(PORT_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    void onAudioPatchListUpdate()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+        remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnAudioPolicyServiceClient::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch (code) {
+    case PORT_LIST_UPDATE: {
+            CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+            onAudioPortListUpdate();
+            return NO_ERROR;
+        } break;
+    case PATCH_LIST_UPDATE: {
+            CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+            onAudioPatchListUpdate();
+            return NO_ERROR;
+        } break;
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 49ff238..afb00aa 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -3,7 +3,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-      SoftAAC2.cpp
+      SoftAAC2.cpp \
+      DrcPresModeWrap.cpp
 
 LOCAL_C_INCLUDES := \
       frameworks/av/media/libstagefright/include \
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
new file mode 100644
index 0000000..129ad65
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2014 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 "DrcPresModeWrap.h"
+
+#include <assert.h>
+
+#define LOG_TAG "SoftAAC2_DrcWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+//#define DRC_PRES_MODE_WRAP_DEBUG
+
+#define GPM_ENCODER_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 64
+
+CDrcPresModeWrapper::CDrcPresModeWrapper()
+{
+    mDataUpdate = true;
+
+    /* Data from streamInfo. */
+    /* Initialized to the same values as in the aac decoder */
+    mStreamPRL = -1;
+    mStreamDRCPresMode = -1;
+    mStreamNrAACChan = 0;
+    mStreamNrOutChan = 0;
+
+    /* Desired values (set by user). */
+    /* Initialized to the same values as in the aac decoder */
+    mDesTarget = -1;
+    mDesAttFactor = 0;
+    mDesBoostFactor = 0;
+    mDesHeavy = 0;
+
+    mEncoderTarget = -1;
+
+    /* Values from last time. */
+    /* Initialized to the same values as the desired values */
+    mLastTarget = -1;
+    mLastAttFactor = 0;
+    mLastBoostFactor = 0;
+    mLastHeavy = 0;
+}
+
+CDrcPresModeWrapper::~CDrcPresModeWrapper()
+{
+}
+
+void
+CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle)
+{
+    mHandleDecoder = handle;
+}
+
+void
+CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo)
+{
+    assert(pStreamInfo);
+
+    if (mStreamPRL != pStreamInfo->drcProgRefLev) {
+        mStreamPRL = pStreamInfo->drcProgRefLev;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL);
+#endif
+    }
+
+    if (mStreamDRCPresMode != pStreamInfo->drcPresMode) {
+        mStreamDRCPresMode = pStreamInfo->drcPresMode;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode);
+#endif
+    }
+
+    if (mStreamNrAACChan != pStreamInfo->aacNumChannels) {
+        mStreamNrAACChan = pStreamInfo->aacNumChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan);
+#endif
+    }
+
+    if (mStreamNrOutChan != pStreamInfo->numChannels) {
+        mStreamNrOutChan = pStreamInfo->numChannels;
+        mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan);
+#endif
+    }
+
+
+
+    if (mStreamNrOutChan<mStreamNrAACChan) {
+        mIsDownmix = true;
+    } else {
+        mIsDownmix = false;
+    }
+
+    if (mIsDownmix && (mStreamNrOutChan == 1)) {
+        mIsMonoDownmix = true;
+    } else {
+        mIsMonoDownmix = false;
+    }
+
+    if (mIsDownmix && mStreamNrOutChan == 2){
+        mIsStereoDownmix = true;
+    } else {
+        mIsStereoDownmix = false;
+    }
+
+}
+
+void
+CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value)
+{
+    switch (param) {
+    case DRC_PRES_MODE_WRAP_DESIRED_TARGET:
+        mDesTarget = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR:
+        mDesAttFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR:
+        mDesBoostFactor = value;
+        break;
+    case DRC_PRES_MODE_WRAP_DESIRED_HEAVY:
+        mDesHeavy = value;
+        break;
+    case DRC_PRES_MODE_WRAP_ENCODER_TARGET:
+        mEncoderTarget = value;
+        break;
+    default:
+        break;
+    }
+    mDataUpdate = true;
+}
+
+void
+CDrcPresModeWrapper::update()
+{
+    // Get Data from Decoder
+    int progRefLevel = mStreamPRL;
+    int drcPresMode = mStreamDRCPresMode;
+
+    // by default, do as desired
+    int newTarget         = mDesTarget;
+    int newAttFactor      = mDesAttFactor;
+    int newBoostFactor    = mDesBoostFactor;
+    int newHeavy          = mDesHeavy;
+
+    if (mDataUpdate) {
+        // sanity check
+        if (mDesTarget < MAX_TARGET_LEVEL){
+            mDesTarget = MAX_TARGET_LEVEL;  // limit target level to -16 dB or below
+            newTarget = MAX_TARGET_LEVEL;
+        }
+
+        if (mEncoderTarget != -1) {
+            if (mDesTarget<124) { // if target level > -31 dB
+                if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                    // no stereo or mono downmixing, calculated scaling of light DRC
+                    /* use as little compression as possible */
+                    newAttFactor = 0;
+                    newBoostFactor = 0;
+                    if (mDesTarget<progRefLevel) { // if target level > PRL
+                        if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level
+                            // mEncoderTarget > target level > PRL
+                            int calcFactor;
+                            float calcFactor_norm;
+                            // 0.0f < calcFactor_norm < 1.0f
+                            calcFactor_norm = (float)(mDesTarget - progRefLevel) /
+                                    (float)(mEncoderTarget - progRefLevel);
+                            calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127
+                            // calcFactor is the lower limit
+                            newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor;
+                            // new AttFactor will be always = calcFactor, as it is set to 0 before.
+                            newBoostFactor = newAttFactor;
+                        } else {
+                            /* target level > mEncoderTarget > PRL */
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //     It is assumed that the framework activates it by default
+                            newAttFactor = 127;
+                            newBoostFactor = 127;
+                        }
+                    } else { // target level <= PRL
+                        // no restrictions required
+                        // newAttFactor = newAttFactor;
+                    }
+                } else { // downmixing
+                    // if target level > -23 dB or mono downmix
+                    if ( (mDesTarget<92) || mIsMonoDownmix ) {
+                        newHeavy = 1;
+                    } else {
+                        // we perform a downmix, so, we need at least full light DRC
+                        newAttFactor = 127;
+                    }
+                }
+            } else { // target level <= -31 dB
+                // playback -31 dB: light DRC only needed if we perform downmixing
+                if (mIsDownmix) {   // we do downmixing
+                    newAttFactor = 127;
+                }
+            }
+        }
+        else { // handle other used encoder target levels
+
+            // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+            if (mStreamNrAACChan > 6) {
+                drcPresMode = 0;
+            }
+
+            switch (drcPresMode) {
+            case 0:
+            default: // presentation mode not indicated
+            {
+
+                if (mDesTarget<124) { // if target level > -31 dB
+                    // no stereo or mono downmixing
+                    if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+                        if (mDesTarget<progRefLevel) { // if target level > PRL
+                            // newTDLimiterEnable = 1;
+                            // the time domain limiter must always be active in this case.
+                            //    It is assumed that the framework activates it by default
+                            newAttFactor = 127; // at least, use light compression
+                        } else { // target level <= PRL
+                            // no restrictions required
+                            // newAttFactor = newAttFactor;
+                        }
+                    } else { // downmixing
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+
+                        // if target level > -23 dB or mono downmix
+                        if ( (mDesTarget < 92) || mIsMonoDownmix ) {
+                            newHeavy = 1;
+                        } else{
+                            // we perform a downmix, so, we need at least full light DRC
+                            newAttFactor = 127;
+                        }
+                    }
+                } else { // target level <= -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        // newTDLimiterEnable = 1;
+                        // the time domain limiter must always be active in this case.
+                        //    It is assumed that the framework activates it by default
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            // Presentation mode 1 and 2 according to ETSI TS 101 154:
+            // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding
+            // in Broadcasting Applications based on the MPEG-2 Transport Stream,
+            // section C.5.4., "Decoding", and Table C.33
+            // ISO DRC            -> newHeavy = 0  (Use light compression, MPEG-style)
+            // Compression_value  -> newHeavy = 1  (Use heavy compression, DVB-style)
+            // scaling restricted -> newAttFactor = 127
+
+            case 1: // presentation mode 1, Light:-31/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    newHeavy = 1;
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            case 2: // presentation mode 2, Light:-23/Heavy:-23
+            {
+                if (mDesTarget < 124) { // if target level > -31 dB
+                    // playback up to -23 dB
+                    if (mIsMonoDownmix) { // if mono downmix
+                        newHeavy = 1;
+                    } else {
+                        newHeavy = 0;
+                        newAttFactor = 127;
+                    }
+                } else { // target level <= -31 dB
+                    // playback -31 dB
+                    newHeavy = 0;
+                    if (mIsDownmix) {   // we do downmixing.
+                        newAttFactor = 127;
+                    }
+                }
+            }
+            break;
+
+            } // switch()
+        } // if (mEncoderTarget  == GPM_ENCODER_TARGET_LEVEL)
+
+        // sanity again
+        if (newHeavy == 1) {
+            newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
+            newAttFactor = 127;
+        }
+
+        // update the decoder
+        if (newTarget != mLastTarget) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget);
+            mLastTarget = newTarget;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newTarget != mDesTarget)
+                ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget);
+            else
+                ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget);
+#endif
+        }
+
+        if (newAttFactor != mLastAttFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor);
+            mLastAttFactor = newAttFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newAttFactor != mDesAttFactor)
+                ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor);
+#endif
+        }
+
+        if (newBoostFactor != mLastBoostFactor) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor);
+            mLastBoostFactor = newBoostFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newBoostFactor != mDesBoostFactor)
+                ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n",
+                        newBoostFactor, mDesBoostFactor);
+            else
+                ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor);
+#endif
+        }
+
+        if (newHeavy != mLastHeavy) {
+            aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy);
+            mLastHeavy = newHeavy;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+            if (newHeavy != mDesHeavy)
+                ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n",
+                        newHeavy, mDesHeavy);
+            else
+                ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy);
+#endif
+        }
+
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+        ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget,
+                newAttFactor, newBoostFactor, newHeavy);
+#endif
+        mDataUpdate = false;
+
+    } // if (mDataUpdate)
+}
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
new file mode 100644
index 0000000..f0b6cf2
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#pragma once
+#include "aacdecoder_lib.h"
+
+typedef enum
+{
+    DRC_PRES_MODE_WRAP_DESIRED_TARGET         = 0x0000,
+    DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR     = 0x0001,
+    DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR   = 0x0002,
+    DRC_PRES_MODE_WRAP_DESIRED_HEAVY          = 0x0003,
+    DRC_PRES_MODE_WRAP_ENCODER_TARGET         = 0x0004
+} DRC_PRES_MODE_WRAP_PARAM;
+
+
+class CDrcPresModeWrapper {
+public:
+    CDrcPresModeWrapper();
+    ~CDrcPresModeWrapper();
+    void setDecoderHandle(const HANDLE_AACDECODER handle);
+    void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value);
+    void submitStreamData(CStreamInfo*);
+    void update();
+
+protected:
+    HANDLE_AACDECODER mHandleDecoder;
+    int mDesTarget;
+    int mDesAttFactor;
+    int mDesBoostFactor;
+    int mDesHeavy;
+
+    int mEncoderTarget;
+
+    int mLastTarget;
+    int mLastAttFactor;
+    int mLastBoostFactor;
+    int mLastHeavy;
+
+    SCHAR mStreamPRL;
+    SCHAR mStreamDRCPresMode;
+    INT mStreamNrAACChan;
+    INT mStreamNrOutChan;
+
+    bool mIsDownmix;
+    bool mIsMonoDownmix;
+    bool mIsStereoDownmix;
+
+    bool mDataUpdate;
+};
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 532e36f..a0e3265 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -25,16 +25,22 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaErrors.h>
 
+#include <math.h>
+
 #define FILEREAD_MAX_LAYERS 2
 
 #define DRC_DEFAULT_MOBILE_REF_LEVEL 64  /* 64*-0.25dB = -16 dB below full scale for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_CUT   127 /* maximum compression of dynamic range for mobile conf */
 #define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1   /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
 #define MAX_CHANNEL_COUNT            8  /* maximum number of audio channels that can be decoded */
 // names of properties that can be used to override the default DRC settings
 #define PROP_DRC_OVERRIDE_REF_LEVEL  "aac_drc_reference_level"
 #define PROP_DRC_OVERRIDE_CUT        "aac_drc_cut"
 #define PROP_DRC_OVERRIDE_BOOST      "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY      "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
 
 namespace android {
 
@@ -57,18 +63,19 @@
       mStreamInfo(NULL),
       mIsADTS(false),
       mInputBufferCount(0),
+      mOutputBufferCount(0),
       mSignalledError(false),
-      mSawInputEos(false),
-      mSignalledOutputEos(false),
-      mAnchorTimeUs(0),
-      mNumSamplesOutput(0),
       mOutputPortSettingsChange(NONE) {
+    for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) {
+        mAnchorTimeUs[i] = 0;
+    }
     initPorts();
     CHECK_EQ(initDecoder(), (status_t)OK);
 }
 
 SoftAAC2::~SoftAAC2() {
     aacDecoder_Close(mAACDecoder);
+    delete mOutputDelayRingBuffer;
 }
 
 void SoftAAC2::initPorts() {
@@ -121,36 +128,72 @@
             status = OK;
         }
     }
-    mDecoderHasData = false;
 
-    // for streams that contain metadata, use the mobile profile DRC settings unless overridden
-    // by platform properties:
+    mEndOfInput = false;
+    mEndOfOutput = false;
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
+    mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+
+    if (mAACDecoder == NULL) {
+        ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
+    }
+
+    //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
+
+    //init DRC wrapper
+    mDrcWrap.setDecoderHandle(mAACDecoder);
+    mDrcWrap.submitStreamData(mStreamInfo);
+
+    // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
+    // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
     char value[PROPERTY_VALUE_MAX];
-    //  * AAC_DRC_REFERENCE_LEVEL
+    //  DRC_PRES_MODE_WRAP_DESIRED_TARGET
     if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) {
         unsigned refLevel = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d",
-                refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel);
+        ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel,
+                DRC_DEFAULT_MOBILE_REF_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL);
     }
-    //  * AAC_DRC_ATTENUATION_FACTOR
+    //  DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
     if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) {
         unsigned cut = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d",
-                        cut, DRC_DEFAULT_MOBILE_DRC_CUT);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut);
+        ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut,
+                DRC_DEFAULT_MOBILE_DRC_CUT);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
     }
-    //  * AAC_DRC_BOOST_FACTOR (note: no default, using cut)
+    //  DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
     if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) {
         unsigned boost = atoi(value);
-        ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost);
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost);
+        ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost,
+                DRC_DEFAULT_MOBILE_DRC_BOOST);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost);
     } else {
-        aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+    }
+    //  DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+    if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) {
+        unsigned heavy = atoi(value);
+        ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy,
+                DRC_DEFAULT_MOBILE_DRC_HEAVY);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy);
+    } else {
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY);
+    }
+    // DRC_PRES_MODE_WRAP_ENCODER_TARGET
+    if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) {
+        unsigned encoderRefLevel = atoi(value);
+        ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d",
+                encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL);
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel);
+    } else {
+        mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
     }
 
     return status;
@@ -290,19 +333,101 @@
     return mInputBufferCount > 0;
 }
 
-void SoftAAC2::maybeConfigureDownmix() const {
-    if (mStreamInfo->numChannels > 2) {
-        char value[PROPERTY_VALUE_MAX];
-        if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
-                (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
-            ALOGI("Downmixing multichannel AAC to stereo");
-            aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
-            mStreamInfo->numChannels = 2;
-            // By default, the decoder creates a 5.1 channel downmix signal
-            // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
-            // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+void SoftAAC2::configureDownmix() const {
+    char value[PROPERTY_VALUE_MAX];
+    if (!(property_get("media.aac_51_output_enabled", value, NULL)
+            && (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
+        ALOGI("limiting to stereo output");
+        aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
+        // By default, the decoder creates a 5.1 channel downmix signal
+        // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
+        // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+    }
+}
+
+bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+    if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
+                    || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
+        }
+
+        if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+        }
+        if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+            ALOGE("RING BUFFER OVERFLOW");
+            return false;
+        }
+    } else {
+        ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
+            mOutputDelayRingBufferWritePos++;
+            if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+            }
+            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+                ALOGE("RING BUFFER OVERFLOW");
+                return false;
+            }
         }
     }
+    return true;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+    if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
+            && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
+                    || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
+        // faster memcopy loop without checks, if the preconditions allow this
+        if (samples != 0) {
+            for (int32_t i = 0; i < numSamples; i++) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
+            }
+        } else {
+            mOutputDelayRingBufferReadPos += numSamples;
+        }
+        if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+            mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+        }
+    } else {
+        ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
+
+        for (int32_t i = 0; i < numSamples; i++) {
+            if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+                ALOGE("RING BUFFER UNDERRUN");
+                return -1;
+            }
+            if (samples != 0) {
+                samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
+            }
+            mOutputDelayRingBufferReadPos++;
+            if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+                mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+            }
+        }
+    }
+    return numSamples;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
+    int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
+    if (available < 0) {
+        available += mOutputDelayRingBufferSize;
+    }
+    if (available < 0) {
+        ALOGE("FATAL RING BUFFER ERROR");
+        return 0;
+    }
+    return available;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+    return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
 }
 
 void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
@@ -318,12 +443,11 @@
     List<BufferInfo *> &outQueue = getPortQueue(1);
 
     if (portIndex == 0 && mInputBufferCount == 0) {
-        ++mInputBufferCount;
-        BufferInfo *info = *inQueue.begin();
-        OMX_BUFFERHEADERTYPE *header = info->mHeader;
+        BufferInfo *inInfo = *inQueue.begin();
+        OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
-        inBuffer[0] = header->pBuffer + header->nOffset;
-        inBufferLength[0] = header->nFilledLen;
+        inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+        inBufferLength[0] = inHeader->nFilledLen;
 
         AAC_DECODER_ERROR decoderErr =
             aacDecoder_ConfigRaw(mAACDecoder,
@@ -331,19 +455,25 @@
                                  inBufferLength);
 
         if (decoderErr != AAC_DEC_OK) {
+            ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
             mSignalledError = true;
             notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
             return;
         }
 
-        inQueue.erase(inQueue.begin());
-        info->mOwnedByUs = false;
-        notifyEmptyBufferDone(header);
+        mInputBufferCount++;
+        mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
 
+        inInfo->mOwnedByUs = false;
+        inQueue.erase(inQueue.begin());
+        inInfo = NULL;
+        notifyEmptyBufferDone(inHeader);
+        inHeader = NULL;
+
+        configureDownmix();
         // Only send out port settings changed event if both sample rate
         // and numChannels are valid.
         if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
-            maybeConfigureDownmix();
             ALOGI("Initially configuring decoder: %d Hz, %d channels",
                 mStreamInfo->sampleRate,
                 mStreamInfo->numChannels);
@@ -355,146 +485,20 @@
         return;
     }
 
-    while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) {
-        BufferInfo *inInfo = NULL;
-        OMX_BUFFERHEADERTYPE *inHeader = NULL;
+    while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
         if (!inQueue.empty()) {
-            inInfo = *inQueue.begin();
-            inHeader = inInfo->mHeader;
-        }
+            INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+            BufferInfo *inInfo = *inQueue.begin();
+            OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
 
-        BufferInfo *outInfo = *outQueue.begin();
-        OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
-        outHeader->nFlags = 0;
-
-        if (inHeader) {
             if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
-                mSawInputEos = true;
-            }
-
-            if (inHeader->nOffset == 0 && inHeader->nFilledLen) {
-                mAnchorTimeUs = inHeader->nTimeStamp;
-                mNumSamplesOutput = 0;
-            }
-
-            if (mIsADTS && inHeader->nFilledLen) {
-                size_t adtsHeaderSize = 0;
-                // skip 30 bits, aac_frame_length follows.
-                // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
-
-                const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
-
-                bool signalError = false;
-                if (inHeader->nFilledLen < 7) {
-                    ALOGE("Audio data too short to contain even the ADTS header. "
-                          "Got %d bytes.", inHeader->nFilledLen);
-                    hexdump(adtsHeader, inHeader->nFilledLen);
-                    signalError = true;
-                } else {
-                    bool protectionAbsent = (adtsHeader[1] & 1);
-
-                    unsigned aac_frame_length =
-                        ((adtsHeader[3] & 3) << 11)
-                        | (adtsHeader[4] << 3)
-                        | (adtsHeader[5] >> 5);
-
-                    if (inHeader->nFilledLen < aac_frame_length) {
-                        ALOGE("Not enough audio data for the complete frame. "
-                              "Got %d bytes, frame size according to the ADTS "
-                              "header is %u bytes.",
-                              inHeader->nFilledLen, aac_frame_length);
-                        hexdump(adtsHeader, inHeader->nFilledLen);
-                        signalError = true;
-                    } else {
-                        adtsHeaderSize = (protectionAbsent ? 7 : 9);
-
-                        inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
-                        inBufferLength[0] = aac_frame_length - adtsHeaderSize;
-
-                        inHeader->nOffset += adtsHeaderSize;
-                        inHeader->nFilledLen -= adtsHeaderSize;
-                    }
-                }
-
-                if (signalError) {
-                    mSignalledError = true;
-
-                    notify(OMX_EventError,
-                           OMX_ErrorStreamCorrupt,
-                           ERROR_MALFORMED,
-                           NULL);
-
-                    return;
-                }
+                mEndOfInput = true;
             } else {
-                inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
-                inBufferLength[0] = inHeader->nFilledLen;
+                mEndOfInput = false;
             }
-        } else {
-            inBufferLength[0] = 0;
-        }
-
-        // Fill and decode
-        INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(
-                outHeader->pBuffer + outHeader->nOffset);
-
-        bytesValid[0] = inBufferLength[0];
-
-        int prevSampleRate = mStreamInfo->sampleRate;
-        int prevNumChannels = mStreamInfo->numChannels;
-
-        AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS;
-        while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
-            mDecoderHasData |= (bytesValid[0] > 0);
-            aacDecoder_Fill(mAACDecoder,
-                            inBuffer,
-                            inBufferLength,
-                            bytesValid);
-
-            decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
-                                                outBuffer,
-                                                outHeader->nAllocLen,
-                                                0 /* flags */);
-            if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
-                if (mSawInputEos && bytesValid[0] <= 0) {
-                    if (mDecoderHasData) {
-                        // flush out the decoder's delayed data by calling DecodeFrame
-                        // one more time, with the AACDEC_FLUSH flag set
-                        decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
-                                                            outBuffer,
-                                                            outHeader->nAllocLen,
-                                                            AACDEC_FLUSH);
-                        mDecoderHasData = false;
-                    }
-                    outHeader->nFlags = OMX_BUFFERFLAG_EOS;
-                    mSignalledOutputEos = true;
-                    break;
-                } else {
-                    ALOGW("Not enough bits, bytesValid %d", bytesValid[0]);
-                }
-            }
-        }
-
-        size_t numOutBytes =
-            mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
-
-        if (inHeader) {
-            if (decoderErr == AAC_DEC_OK) {
-                UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
-                inHeader->nFilledLen -= inBufferUsedLength;
-                inHeader->nOffset += inBufferUsedLength;
-            } else {
-                ALOGW("AAC decoder returned error %d, substituting silence",
-                      decoderErr);
-
-                memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
-
-                // Discard input buffer.
-                inHeader->nFilledLen = 0;
-
-                aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
-                // fall through
+            if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen?
+                mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] =
+                        inHeader->nTimeStamp;
             }
 
             if (inHeader->nFilledLen == 0) {
@@ -503,54 +507,282 @@
                 inInfo = NULL;
                 notifyEmptyBufferDone(inHeader);
                 inHeader = NULL;
+            } else {
+                if (mIsADTS) {
+                    size_t adtsHeaderSize = 0;
+                    // skip 30 bits, aac_frame_length follows.
+                    // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+                    const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+                    bool signalError = false;
+                    if (inHeader->nFilledLen < 7) {
+                        ALOGE("Audio data too short to contain even the ADTS header. "
+                                "Got %d bytes.", inHeader->nFilledLen);
+                        hexdump(adtsHeader, inHeader->nFilledLen);
+                        signalError = true;
+                    } else {
+                        bool protectionAbsent = (adtsHeader[1] & 1);
+
+                        unsigned aac_frame_length =
+                            ((adtsHeader[3] & 3) << 11)
+                            | (adtsHeader[4] << 3)
+                            | (adtsHeader[5] >> 5);
+
+                        if (inHeader->nFilledLen < aac_frame_length) {
+                            ALOGE("Not enough audio data for the complete frame. "
+                                    "Got %d bytes, frame size according to the ADTS "
+                                    "header is %u bytes.",
+                                    inHeader->nFilledLen, aac_frame_length);
+                            hexdump(adtsHeader, inHeader->nFilledLen);
+                            signalError = true;
+                        } else {
+                            adtsHeaderSize = (protectionAbsent ? 7 : 9);
+
+                            inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+                            inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+
+                            inHeader->nOffset += adtsHeaderSize;
+                            inHeader->nFilledLen -= adtsHeaderSize;
+                        }
+                    }
+
+                    if (signalError) {
+                        mSignalledError = true;
+
+                        notify(OMX_EventError,
+                               OMX_ErrorStreamCorrupt,
+                               ERROR_MALFORMED,
+                               NULL);
+
+                        return;
+                    }
+                } else {
+                    inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+                    inBufferLength[0] = inHeader->nFilledLen;
+                }
+
+                // Fill and decode
+                bytesValid[0] = inBufferLength[0];
+
+                INT prevSampleRate = mStreamInfo->sampleRate;
+                INT prevNumChannels = mStreamInfo->numChannels;
+
+                aacDecoder_Fill(mAACDecoder,
+                                inBuffer,
+                                inBufferLength,
+                                bytesValid);
+
+                 // run DRC check
+                 mDrcWrap.submitStreamData(mStreamInfo);
+                 mDrcWrap.update();
+
+                AAC_DECODER_ERROR decoderErr =
+                    aacDecoder_DecodeFrame(mAACDecoder,
+                                           tmpOutBuffer,
+                                           2048 * MAX_CHANNEL_COUNT,
+                                           0 /* flags */);
+
+                if (decoderErr != AAC_DEC_OK) {
+                    ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+                }
+
+                if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+                    ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                    return;
+                }
+
+                if (bytesValid[0] != 0) {
+                    ALOGE("bytesValid[0] != 0 should never happen");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                    return;
+                }
+
+                size_t numOutBytes =
+                    mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+                if (decoderErr == AAC_DEC_OK) {
+                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+                    UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+                    inHeader->nFilledLen -= inBufferUsedLength;
+                    inHeader->nOffset += inBufferUsedLength;
+                } else {
+                    ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+                    memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+                    if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+                            mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+
+                    // Discard input buffer.
+                    inHeader->nFilledLen = 0;
+
+                    aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+                    // fall through
+                }
+
+                /*
+                 * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+                 * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+                 * rate system and the sampling rate in the final output is actually
+                 * doubled compared with the core AAC decoder sampling rate.
+                 *
+                 * Explicit signalling is done by explicitly defining SBR audio object
+                 * type in the bitstream. Implicit signalling is done by embedding
+                 * SBR content in AAC extension payload specific to SBR, and hence
+                 * requires an AAC decoder to perform pre-checks on actual audio frames.
+                 *
+                 * Thus, we could not say for sure whether a stream is
+                 * AAC+/eAAC+ until the first data frame is decoded.
+                 */
+                if (mOutputBufferCount > 1) {
+                    if (mStreamInfo->sampleRate != prevSampleRate ||
+                        mStreamInfo->numChannels != prevNumChannels) {
+                        ALOGE("can not reconfigure AAC output");
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                        return;
+                    }
+                }
+                if (mInputBufferCount <= 2) { // TODO: <= 1
+                    if (mStreamInfo->sampleRate != prevSampleRate ||
+                        mStreamInfo->numChannels != prevNumChannels) {
+                        ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+                              prevSampleRate, mStreamInfo->sampleRate,
+                              prevNumChannels, mStreamInfo->numChannels);
+
+                        notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+                        mOutputPortSettingsChange = AWAITING_DISABLED;
+
+                        if (inHeader->nFilledLen == 0) {
+                            inInfo->mOwnedByUs = false;
+                            mInputBufferCount++;
+                            inQueue.erase(inQueue.begin());
+                            inInfo = NULL;
+                            notifyEmptyBufferDone(inHeader);
+                            inHeader = NULL;
+                        }
+                        return;
+                    }
+                } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+                    ALOGW("Invalid AAC stream");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+                    return;
+                }
+                if (inHeader->nFilledLen == 0) {
+                    inInfo->mOwnedByUs = false;
+                    mInputBufferCount++;
+                    inQueue.erase(inQueue.begin());
+                    inInfo = NULL;
+                    notifyEmptyBufferDone(inHeader);
+                    inHeader = NULL;
+                } else {
+                    ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+                }
             }
         }
 
-        /*
-         * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
-         * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
-         * rate system and the sampling rate in the final output is actually
-         * doubled compared with the core AAC decoder sampling rate.
-         *
-         * Explicit signalling is done by explicitly defining SBR audio object
-         * type in the bitstream. Implicit signalling is done by embedding
-         * SBR content in AAC extension payload specific to SBR, and hence
-         * requires an AAC decoder to perform pre-checks on actual audio frames.
-         *
-         * Thus, we could not say for sure whether a stream is
-         * AAC+/eAAC+ until the first data frame is decoded.
-         */
-        if (mInputBufferCount <= 2) {
-            if (mStreamInfo->sampleRate != prevSampleRate ||
-                mStreamInfo->numChannels != prevNumChannels) {
-                maybeConfigureDownmix();
-                ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
-                      prevSampleRate, mStreamInfo->sampleRate,
-                      prevNumChannels, mStreamInfo->numChannels);
+        int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
 
-                notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
-                mOutputPortSettingsChange = AWAITING_DISABLED;
+        if (!mEndOfInput && mOutputDelayCompensated < outputDelay) {
+            // discard outputDelay at the beginning
+            int32_t toCompensate = outputDelay - mOutputDelayCompensated;
+            int32_t discard = outputDelayRingBufferSamplesAvailable();
+            if (discard > toCompensate) {
+                discard = toCompensate;
+            }
+            int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
+            mOutputDelayCompensated += discarded;
+            continue;
+        }
+
+        if (mEndOfInput) {
+            while (mOutputDelayCompensated > 0) {
+                // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+                INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+ 
+                 // run DRC check
+                 mDrcWrap.submitStreamData(mStreamInfo);
+                 mDrcWrap.update();
+
+                AAC_DECODER_ERROR decoderErr =
+                    aacDecoder_DecodeFrame(mAACDecoder,
+                                           tmpOutBuffer,
+                                           2048 * MAX_CHANNEL_COUNT,
+                                           AACDEC_FLUSH);
+                if (decoderErr != AAC_DEC_OK) {
+                    ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+                }
+
+                int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+                if (tmpOutBufferSamples > mOutputDelayCompensated) {
+                    tmpOutBufferSamples = mOutputDelayCompensated;
+                }
+                outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+                mOutputDelayCompensated -= tmpOutBufferSamples;
+            }
+        }
+
+        while (!outQueue.empty()
+                && outputDelayRingBufferSamplesAvailable()
+                        >= mStreamInfo->frameSize * mStreamInfo->numChannels) {
+            BufferInfo *outInfo = *outQueue.begin();
+            OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+            if (outHeader->nOffset != 0) {
+                ALOGE("outHeader->nOffset != 0 is not handled");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
                 return;
             }
-        } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
-            ALOGW("Invalid AAC stream");
-            mSignalledError = true;
-            notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
-            return;
-        }
 
-        if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
-            // We'll only output data if we successfully decoded it or
-            // we've previously decoded valid data, in the latter case
-            // (decode failed) we'll output a silent frame.
-            outHeader->nFilledLen = numOutBytes;
+            INT_PCM *outBuffer =
+                    reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+            if (outHeader->nOffset
+                    + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
+                    > outHeader->nAllocLen) {
+                ALOGE("buffer overflow");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
 
-            outHeader->nTimeStamp =
-                mAnchorTimeUs
-                    + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate;
+            }
+            int32_t ns = outputDelayRingBufferGetSamples(outBuffer,
+                    mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow
+            if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
 
-            mNumSamplesOutput += mStreamInfo->frameSize;
+            outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
+                    * sizeof(int16_t);
+            if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+                outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+                mEndOfOutput = true;
+            } else {
+                outHeader->nFlags = 0;
+            }
 
+            outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+                    % kNumDelayBlocksMax];
+
+            mOutputBufferCount++;
             outInfo->mOwnedByUs = false;
             outQueue.erase(outQueue.begin());
             outInfo = NULL;
@@ -558,8 +790,48 @@
             outHeader = NULL;
         }
 
-        if (decoderErr == AAC_DEC_OK) {
-            ++mInputBufferCount;
+        if (mEndOfInput) {
+            if (outputDelayRingBufferSamplesAvailable() > 0
+                    && outputDelayRingBufferSamplesAvailable()
+                            < mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+                mSignalledError = true;
+                notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                return;
+            }
+
+            if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+                if (!mEndOfOutput) {
+                    // send empty block signaling EOS
+                    mEndOfOutput = true;
+                    BufferInfo *outInfo = *outQueue.begin();
+                    OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+                    if (outHeader->nOffset != 0) {
+                        ALOGE("outHeader->nOffset != 0 is not handled");
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+                        return;
+                    }
+
+                    INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer
+                            + outHeader->nOffset);
+                    int32_t ns = 0;
+                    outHeader->nFilledLen = 0;
+                    outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+                    outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+                            % kNumDelayBlocksMax];
+
+                    mOutputBufferCount++;
+                    outInfo->mOwnedByUs = false;
+                    outQueue.erase(outQueue.begin());
+                    outInfo = NULL;
+                    notifyFillBufferDone(outHeader);
+                    outHeader = NULL;
+                }
+                break; // if outQueue not empty but no more output
+            }
         }
     }
 }
@@ -574,34 +846,67 @@
         // but only if initialization has already happened.
         if (mInputBufferCount != 0) {
             mInputBufferCount = 1;
-            mStreamInfo->sampleRate = 0;
         }
+    } else {
+        while (outputDelayRingBufferSamplesAvailable() > 0) {
+            int32_t ns = outputDelayRingBufferGetSamples(0,
+                    mStreamInfo->frameSize * mStreamInfo->numChannels);
+            if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+                ALOGE("not a complete frame of samples available");
+            }
+            mOutputBufferCount++;
+        }
+        mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
     }
 }
 
 void SoftAAC2::drainDecoder() {
-    // a buffer big enough for 6 channels of decoded HE-AAC
-    short buf [2048*6];
-    aacDecoder_DecodeFrame(mAACDecoder,
-            buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
-    aacDecoder_DecodeFrame(mAACDecoder,
-            buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
-    aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-    mDecoderHasData = false;
+    int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+    // flush decoder until outputDelay is compensated
+    while (mOutputDelayCompensated > 0) {
+        // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+        INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+        // run DRC check
+        mDrcWrap.submitStreamData(mStreamInfo);
+        mDrcWrap.update();
+
+        AAC_DECODER_ERROR decoderErr =
+            aacDecoder_DecodeFrame(mAACDecoder,
+                                   tmpOutBuffer,
+                                   2048 * MAX_CHANNEL_COUNT,
+                                   AACDEC_FLUSH);
+        if (decoderErr != AAC_DEC_OK) {
+            ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+        }
+
+        int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+        if (tmpOutBufferSamples > mOutputDelayCompensated) {
+            tmpOutBufferSamples = mOutputDelayCompensated;
+        }
+        outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+
+        mOutputDelayCompensated -= tmpOutBufferSamples;
+    }
 }
 
 void SoftAAC2::onReset() {
     drainDecoder();
     // reset the "configured" state
     mInputBufferCount = 0;
-    mNumSamplesOutput = 0;
+    mOutputBufferCount = 0;
+    mOutputDelayCompensated = 0;
+    mOutputDelayRingBufferWritePos = 0;
+    mOutputDelayRingBufferReadPos = 0;
+    mEndOfInput = false;
+    mEndOfOutput = false;
+
     // To make the codec behave the same before and after a reset, we need to invalidate the
     // streaminfo struct. This does that:
-    mStreamInfo->sampleRate = 0;
+    mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
 
     mSignalledError = false;
-    mSawInputEos = false;
-    mSignalledOutputEos = false;
     mOutputPortSettingsChange = NONE;
 }
 
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index a7ea1e2..5cde03a 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -20,6 +20,7 @@
 #include "SimpleSoftOMXComponent.h"
 
 #include "aacdecoder_lib.h"
+#include "DrcPresModeWrap.h"
 
 namespace android {
 
@@ -47,18 +48,19 @@
     enum {
         kNumInputBuffers        = 4,
         kNumOutputBuffers       = 4,
+        kNumDelayBlocksMax      = 8,
     };
 
     HANDLE_AACDECODER mAACDecoder;
     CStreamInfo *mStreamInfo;
     bool mIsADTS;
-    bool mDecoderHasData;
+    bool mIsFirst;
     size_t mInputBufferCount;
+    size_t mOutputBufferCount;
     bool mSignalledError;
-    bool mSawInputEos;
-    bool mSignalledOutputEos;
-    int64_t mAnchorTimeUs;
-    int64_t mNumSamplesOutput;
+    int64_t mAnchorTimeUs[kNumDelayBlocksMax];
+
+    CDrcPresModeWrapper mDrcWrap;
 
     enum {
         NONE,
@@ -69,9 +71,22 @@
     void initPorts();
     status_t initDecoder();
     bool isConfigured() const;
-    void maybeConfigureDownmix() const;
+    void configureDownmix() const;
     void drainDecoder();
 
+//      delay compensation
+    bool mEndOfInput;
+    bool mEndOfOutput;
+    int32_t mOutputDelayCompensated;
+    int32_t mOutputDelayRingBufferSize;
+    short *mOutputDelayRingBuffer;
+    int32_t mOutputDelayRingBufferWritePos;
+    int32_t mOutputDelayRingBufferReadPos;
+    bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
+    int32_t outputDelayRingBufferSamplesAvailable();
+    int32_t outputDelayRingBufferSamplesLeft();
+
     DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
 };
 
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index f7b6f64..0bdf5a3 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -64,6 +64,7 @@
 
 LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
 LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp
+LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp
 
 LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
 
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8d57451..ace3bf1 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -750,6 +750,9 @@
     name -= TRACK0;
     ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
 
+    if (mState.tracks[name].mInputBufferProvider == bufferProvider) {
+        return; // don't reset any buffer providers if identical.
+    }
     if (mState.tracks[name].mReformatBufferProvider != NULL) {
         mState.tracks[name].mReformatBufferProvider->reset();
     } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
new file mode 100644
index 0000000..0c9b976
--- /dev/null
+++ b/services/audioflinger/FastCapture.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 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 "FastCapture"
+//#define LOG_NDEBUG 0
+
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <media/AudioBufferProvider.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "FastCapture.h"
+
+namespace android {
+
+/*static*/ const FastCaptureState FastCapture::initial;
+
+FastCapture::FastCapture() : FastThread(),
+    inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0),
+    readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0),
+    // dummyDumpState
+    totalNativeFramesRead(0)
+{
+    previous = &initial;
+    current = &initial;
+
+    mDummyDumpState = &dummyDumpState;
+}
+
+FastCapture::~FastCapture()
+{
+}
+
+FastCaptureStateQueue* FastCapture::sq()
+{
+    return &mSQ;
+}
+
+const FastThreadState *FastCapture::poll()
+{
+    return mSQ.poll();
+}
+
+void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+{
+}
+
+void FastCapture::onIdle()
+{
+    preIdle = *(const FastCaptureState *)current;
+    current = &preIdle;
+}
+
+void FastCapture::onExit()
+{
+    delete[] readBuffer;
+}
+
+bool FastCapture::isSubClassCommand(FastThreadState::Command command)
+{
+    switch ((FastCaptureState::Command) command) {
+    case FastCaptureState::READ:
+    case FastCaptureState::WRITE:
+    case FastCaptureState::READ_WRITE:
+        return true;
+    default:
+        return false;
+    }
+}
+
+void FastCapture::onStateChange()
+{
+    const FastCaptureState * const current = (const FastCaptureState *) this->current;
+    const FastCaptureState * const previous = (const FastCaptureState *) this->previous;
+    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+    const size_t frameCount = current->mFrameCount;
+
+    bool eitherChanged = false;
+
+    // check for change in input HAL configuration
+    NBAIO_Format previousFormat = format;
+    if (current->mInputSourceGen != inputSourceGen) {
+        inputSource = current->mInputSource;
+        inputSourceGen = current->mInputSourceGen;
+        if (inputSource == NULL) {
+            format = Format_Invalid;
+            sampleRate = 0;
+        } else {
+            format = inputSource->format();
+            sampleRate = Format_sampleRate(format);
+            unsigned channelCount = Format_channelCount(format);
+            ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+        }
+        dumpState->mSampleRate = sampleRate;
+        eitherChanged = true;
+    }
+
+    // check for change in pipe
+    if (current->mPipeSinkGen != pipeSinkGen) {
+        pipeSink = current->mPipeSink;
+        pipeSinkGen = current->mPipeSinkGen;
+        eitherChanged = true;
+    }
+
+    // input source and pipe sink must be compatible
+    if (eitherChanged && inputSource != NULL && pipeSink != NULL) {
+        ALOG_ASSERT(Format_isEqual(format, pipeSink->format()));
+    }
+
+    if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
+        // FIXME to avoid priority inversion, don't delete here
+        delete[] readBuffer;
+        readBuffer = NULL;
+        if (frameCount > 0 && sampleRate > 0) {
+            // FIXME new may block for unbounded time at internal mutex of the heap
+            //       implementation; it would be better to have normal capture thread allocate for
+            //       us to avoid blocking here and to prevent possible priority inversion
+            unsigned channelCount = Format_channelCount(format);
+            // FIXME frameSize
+            readBuffer = new short[frameCount * channelCount];
+            periodNs = (frameCount * 1000000000LL) / sampleRate;    // 1.00
+            underrunNs = (frameCount * 1750000000LL) / sampleRate;  // 1.75
+            overrunNs = (frameCount * 500000000LL) / sampleRate;    // 0.50
+            forceNs = (frameCount * 950000000LL) / sampleRate;      // 0.95
+            warmupNs = (frameCount * 500000000LL) / sampleRate;     // 0.50
+        } else {
+            periodNs = 0;
+            underrunNs = 0;
+            overrunNs = 0;
+            forceNs = 0;
+            warmupNs = 0;
+        }
+        readBufferState = -1;
+        dumpState->mFrameCount = frameCount;
+    }
+
+}
+
+void FastCapture::onWork()
+{
+    const FastCaptureState * const current = (const FastCaptureState *) this->current;
+    FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+    const FastCaptureState::Command command = this->command;
+    const size_t frameCount = current->mFrameCount;
+
+    if ((command & FastCaptureState::READ) /*&& isWarm*/) {
+        ALOG_ASSERT(inputSource != NULL);
+        ALOG_ASSERT(readBuffer != NULL);
+        dumpState->mReadSequence++;
+        ATRACE_BEGIN("read");
+        ssize_t framesRead = inputSource->read(readBuffer, frameCount,
+                AudioBufferProvider::kInvalidPTS);
+        ATRACE_END();
+        dumpState->mReadSequence++;
+        if (framesRead >= 0) {
+            LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
+            totalNativeFramesRead += framesRead;
+            dumpState->mFramesRead = totalNativeFramesRead;
+            readBufferState = framesRead;
+        } else {
+            dumpState->mReadErrors++;
+            readBufferState = 0;
+        }
+        // FIXME rename to attemptedIO
+        attemptedWrite = true;
+    }
+
+    if (command & FastCaptureState::WRITE) {
+        ALOG_ASSERT(pipeSink != NULL);
+        ALOG_ASSERT(readBuffer != NULL);
+        if (readBufferState < 0) {
+            unsigned channelCount = Format_channelCount(format);
+            // FIXME frameSize
+            memset(readBuffer, 0, frameCount * channelCount * sizeof(short));
+            readBufferState = frameCount;
+        }
+        if (readBufferState > 0) {
+            ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState);
+            // FIXME This supports at most one fast capture client.
+            //       To handle multiple clients this could be converted to an array,
+            //       or with a lot more work the control block could be shared by all clients.
+            audio_track_cblk_t* cblk = current->mCblk;
+            if (cblk != NULL && framesWritten > 0) {
+                int32_t rear = cblk->u.mStreaming.mRear;
+                android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear);
+                cblk->mServer += framesWritten;
+                int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+                if (!(old & CBLK_FUTEX_WAKE)) {
+                    // client is never in server process, so don't use FUTEX_WAKE_PRIVATE
+                    (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1);
+                }
+            }
+        }
+    }
+}
+
+FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(),
+    mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0)
+{
+}
+
+FastCaptureDumpState::~FastCaptureDumpState()
+{
+}
+
+}   // namespace android
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
new file mode 100644
index 0000000..e535b9d
--- /dev/null
+++ b/services/audioflinger/FastCapture.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_CAPTURE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_H
+
+#include "FastThread.h"
+#include "StateQueue.h"
+#include "FastCaptureState.h"
+
+namespace android {
+
+typedef StateQueue<FastCaptureState> FastCaptureStateQueue;
+
+struct FastCaptureDumpState : FastThreadDumpState {
+    FastCaptureDumpState();
+    /*virtual*/ ~FastCaptureDumpState();
+
+    // FIXME by renaming, could pull up many of these to FastThreadDumpState
+    uint32_t mReadSequence;     // incremented before and after each read()
+    uint32_t mFramesRead;       // total number of frames read successfully
+    uint32_t mReadErrors;       // total number of read() errors
+    uint32_t mSampleRate;
+    size_t   mFrameCount;
+};
+
+class FastCapture : public FastThread {
+
+public:
+            FastCapture();
+    virtual ~FastCapture();
+
+            FastCaptureStateQueue*  sq();
+
+private:
+            FastCaptureStateQueue   mSQ;
+
+    // callouts
+    virtual const FastThreadState *poll();
+    virtual void setLog(NBLog::Writer *logWriter);
+    virtual void onIdle();
+    virtual void onExit();
+    virtual bool isSubClassCommand(FastThreadState::Command command);
+    virtual void onStateChange();
+    virtual void onWork();
+
+    static const FastCaptureState initial;
+    FastCaptureState preIdle; // copy of state before we went into idle
+    // FIXME by renaming, could pull up many of these to FastThread
+    NBAIO_Source *inputSource;
+    int inputSourceGen;
+    NBAIO_Sink *pipeSink;
+    int pipeSinkGen;
+    short *readBuffer;
+    ssize_t readBufferState;    // number of initialized frames in readBuffer, or -1 to clear
+    NBAIO_Format format;
+    unsigned sampleRate;
+    FastCaptureDumpState dummyDumpState;
+    uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead
+
+};  // class FastCapture
+
+}   // namespace android
+
+#endif  // ANDROID_AUDIO_FAST_CAPTURE_H
diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp
new file mode 100644
index 0000000..1d029b7
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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 "FastCaptureState.h"
+
+namespace android {
+
+FastCaptureState::FastCaptureState() : FastThreadState(),
+    mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mFrameCount(0)
+{
+}
+
+FastCaptureState::~FastCaptureState()
+{
+}
+
+}   // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
new file mode 100644
index 0000000..29c865a
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+
+#include <media/nbaio/NBAIO.h>
+#include "FastThreadState.h"
+#include <private/media/AudioTrackShared.h>
+
+namespace android {
+
+// Represent a single state of the fast capture
+struct FastCaptureState : FastThreadState {
+                FastCaptureState();
+    /*virtual*/ ~FastCaptureState();
+
+    // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread
+    NBAIO_Source    *mInputSource;      // HAL input device, must already be negotiated
+    // FIXME by renaming, could pull up these fields to FastThreadState
+    int             mInputSourceGen;    // increment when mInputSource is assigned
+    NBAIO_Sink      *mPipeSink;         // after reading from input source, write to this pipe sink
+    int             mPipeSinkGen;       // increment when mPipeSink is assigned
+    size_t          mFrameCount;        // number of frames per fast capture buffer
+    audio_track_cblk_t  *mCblk;         // control block for the single fast client, or NULL
+
+    // Extends FastThreadState::Command
+    static const Command
+        // The following commands also process configuration changes, and can be "or"ed:
+        READ = 0x8,             // read from input source
+        WRITE = 0x10,           // write to pipe sink
+        READ_WRITE = 0x18;      // read from input source and write to pipe sink
+
+};  // struct FastCaptureState
+
+}   // namespace android
+
+#endif  // ANDROID_AUDIO_FAST_CAPTURE_STATE_H
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index c840418..13b21ec 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -36,6 +36,7 @@
 #include <cpustats/ThreadCpuUsage.h>
 #endif
 #endif
+#include <audio_utils/format.h>
 #include "AudioMixer.h"
 #include "FastMixer.h"
 
@@ -52,7 +53,11 @@
     outputSink(NULL),
     outputSinkGen(0),
     mixer(NULL),
+    mSinkBuffer(NULL),
+    mSinkBufferSize(0),
     mMixerBuffer(NULL),
+    mMixerBufferSize(0),
+    mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT),
     mMixerBufferState(UNDEFINED),
     format(Format_Invalid),
     sampleRate(0),
@@ -108,7 +113,8 @@
 void FastMixer::onExit()
 {
     delete mixer;
-    delete[] mMixerBuffer;
+    free(mMixerBuffer);
+    free(mSinkBuffer);
 }
 
 bool FastMixer::isSubClassCommand(FastThreadState::Command command)
@@ -154,14 +160,23 @@
         // FIXME to avoid priority inversion, don't delete here
         delete mixer;
         mixer = NULL;
-        delete[] mMixerBuffer;
+        free(mMixerBuffer);
         mMixerBuffer = NULL;
+        free(mSinkBuffer);
+        mSinkBuffer = NULL;
         if (frameCount > 0 && sampleRate > 0) {
             // FIXME new may block for unbounded time at internal mutex of the heap
             //       implementation; it would be better to have normal mixer allocate for us
             //       to avoid blocking here and to prevent possible priority inversion
             mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
-            mMixerBuffer = new short[frameCount * FCC_2];
+            const size_t mixerFrameSize = FCC_2 * audio_bytes_per_sample(mMixerBufferFormat);
+            mMixerBufferSize = mixerFrameSize * frameCount;
+            (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
+            const size_t sinkFrameSize = FCC_2 * audio_bytes_per_sample(format.mFormat);
+            if (sinkFrameSize > mixerFrameSize) { // need a sink buffer
+                mSinkBufferSize = sinkFrameSize * frameCount;
+                (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize);
+            }
             periodNs = (frameCount * 1000000000LL) / sampleRate;    // 1.00
             underrunNs = (frameCount * 1750000000LL) / sampleRate;  // 1.75
             overrunNs = (frameCount * 500000000LL) / sampleRate;    // 0.50
@@ -231,6 +246,10 @@
                 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
                         (void *) mMixerBuffer);
                 // newly allocated track names default to full scale volume
+                mixer->setParameter(
+                        name,
+                        AudioMixer::TRACK,
+                        AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
                 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
                         (void *)(uintptr_t)fastTrack->mFormat);
                 mixer->enable(name);
@@ -261,6 +280,10 @@
                     }
                     mixer->setParameter(name, AudioMixer::RESAMPLE,
                             AudioMixer::REMOVE, NULL);
+                    mixer->setParameter(
+                            name,
+                            AudioMixer::TRACK,
+                            AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
                     mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
                             (void *)(uintptr_t)fastTrack->mFormat);
                     mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
@@ -369,9 +392,14 @@
     //bool didFullWrite = false;    // dumpsys could display a count of partial writes
     if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) {
         if (mMixerBufferState == UNDEFINED) {
-            memset(mMixerBuffer, 0, frameCount * FCC_2 * sizeof(short));
+            memset(mMixerBuffer, 0, mMixerBufferSize);
             mMixerBufferState = ZEROED;
         }
+        void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
+        if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
+            memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat,
+                    frameCount * Format_channelCount(format));
+        }
         // if non-NULL, then duplicate write() to this non-blocking sink
         NBAIO_Sink* teeSink;
         if ((teeSink = current->mTeeSink) != NULL) {
@@ -381,7 +409,7 @@
         //       but this code should be modified to handle both non-blocking and blocking sinks
         dumpState->mWriteSequence++;
         ATRACE_BEGIN("write");
-        ssize_t framesWritten = outputSink->write(mMixerBuffer, frameCount);
+        ssize_t framesWritten = outputSink->write(buffer, frameCount);
         ATRACE_END();
         dumpState->mWriteSequence++;
         if (framesWritten >= 0) {
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index db3e2c9..4671670 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -61,7 +61,15 @@
     NBAIO_Sink *outputSink;
     int outputSinkGen;
     AudioMixer* mixer;
-    short *mMixerBuffer;
+
+    // mSinkBuffer audio format is stored in format.mFormat.
+    void* mSinkBuffer;                  // used for mixer output format translation
+                                        // if sink format is different than mixer output.
+    size_t mSinkBufferSize;
+    void* mMixerBuffer;                 // mixer output buffer.
+    size_t mMixerBufferSize;
+    audio_format_t mMixerBufferFormat;  // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT).
+
     enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState;
     NBAIO_Format format;
     unsigned sampleRate;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index eee74b3..96a8127 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -179,11 +179,11 @@
                 ALOGW("createAudioPatch() bad src hw module %d", src_module);
                 return BAD_VALUE;
             }
+            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
             for (unsigned int i = 0; i < patch->num_sinks; i++) {
-                // limit to connections between devices and output streams
-                if (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX) {
-                    ALOGW("createAudioPatch() invalid sink type %d for device source",
-                          patch->sinks[i].type);
+                // reject connection to different sink types
+                if (patch->sinks[i].type != patch->sinks[0].type) {
+                    ALOGW("createAudioPatch() different sink types in same patch not supported");
                     return BAD_VALUE;
                 }
                 // limit to connections between sinks and sources on same HW module
@@ -192,9 +192,16 @@
                             "sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module);
                     return BAD_VALUE;
                 }
+
+                // limit to connections between devices and output streams for HAL before 3.0
+                if ((audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
+                        (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
+                    ALOGW("createAudioPatch() invalid sink type %d for device source",
+                          patch->sinks[i].type);
+                    return BAD_VALUE;
+                }
             }
 
-            AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
             if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
                 if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                     sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
@@ -397,13 +404,38 @@
 }
 
 /* Set audio port configuration */
-status_t AudioFlinger::PatchPanel::setAudioPortConfig(
-        const struct audio_port_config *config __unused)
+status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
 {
     ALOGV("setAudioPortConfig");
+    status_t status = NO_ERROR;
+
+    sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+    if (audioflinger == 0) {
+        return NO_INIT;
+    }
+
+    audio_module_handle_t module;
+    if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        module = config->ext.device.hw_module;
+    } else {
+        module = config->ext.mix.hw_module;
+    }
+
+    ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
+    if (index < 0) {
+        ALOGW("setAudioPortConfig() bad hw module %d", module);
+        return BAD_VALUE;
+    }
+
+    AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+    if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+        audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+        return hwDevice->set_audio_port_config(hwDevice, config);
+    } else {
+        return INVALID_OPERATION;
+    }
     return NO_ERROR;
 }
 
 
-
 }; // namespace android
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 0d5cd0c..6f4505e 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -16,12 +16,14 @@
 
 #include "Configuration.h"
 #include "FastMixerState.h"
+#include "FastCaptureState.h"
 #include "StateQueue.h"
 
 // FIXME hack for gcc
 
 namespace android {
 
-template class StateQueue<FastMixerState>;  // typedef FastMixerStateQueue
+template class StateQueue<FastMixerState>;      // typedef FastMixerStateQueue
+template class StateQueue<FastCaptureState>;    // typedef FastCaptureStateQueue
 
 }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 576350e..742163b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2744,9 +2744,27 @@
         break;
     }
     if (initFastMixer) {
+        audio_format_t fastMixerFormat;
+        if (mMixerBufferEnabled && mEffectBufferEnabled) {
+            fastMixerFormat = AUDIO_FORMAT_PCM_FLOAT;
+        } else {
+            fastMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
+        }
+        if (mFormat != fastMixerFormat) {
+            // change our Sink format to accept our intermediate precision
+            mFormat = fastMixerFormat;
+            free(mSinkBuffer);
+            mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat);
+            const size_t sinkBufferSize = mNormalFrameCount * mFrameSize;
+            (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize);
+        }
 
         // create a MonoPipe to connect our submix to FastMixer
         NBAIO_Format format = mOutputSink->format();
+        // adjust format to match that of the Fast Mixer
+        format.mFormat = fastMixerFormat;
+        format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount;
+
         // This pipe depth compensates for scheduling latency of the normal mixer thread.
         // When it wakes up after a maximum latency, it runs a few cycles quickly before
         // finally blocking.  Note the pipe implementation rounds up the request to a power of 2.
diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index 8225e36..c322d92 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -195,4 +195,21 @@
     return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
 }
 
+status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig(
+                                                        const struct audio_port_config *config,
+                                                        int delayMs)
+{
+    return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs);
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
+{
+    mAudioPolicyService->onAudioPortListUpdate();
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate()
+{
+    mAudioPolicyService->onAudioPatchListUpdate();
+}
+
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index bb2deb6..c025a45 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -162,6 +162,24 @@
     virtual status_t    dump(int fd) = 0;
 
     virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0;
+
+    virtual status_t listAudioPorts(audio_port_role_t role,
+                                    audio_port_type_t type,
+                                    unsigned int *num_ports,
+                                    struct audio_port *ports,
+                                    unsigned int *generation) = 0;
+    virtual status_t getAudioPort(struct audio_port *port) = 0;
+    virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                       audio_patch_handle_t *handle,
+                                       uid_t uid) = 0;
+    virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                          uid_t uid) = 0;
+    virtual status_t listAudioPatches(unsigned int *num_patches,
+                                      struct audio_patch *patches,
+                                      unsigned int *generation) = 0;
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+    virtual void clearAudioPatches(uid_t uid) = 0;
+
 };
 
 
@@ -255,6 +273,12 @@
     virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                        int delayMs) = 0;
 
+    /* Set audio port configuration */
+    virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs) = 0;
+
+    virtual void onAudioPortListUpdate() = 0;
+
+    virtual void onAudioPatchListUpdate() = 0;
 };
 
 extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
index 7cd253b..2b33703 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
@@ -463,43 +463,72 @@
     return mAudioPolicyManager->isOffloadSupported(info);
 }
 
-status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused,
-                                            audio_port_type_t type __unused,
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role,
+                                            audio_port_type_t type,
                                             unsigned int *num_ports,
-                                            struct audio_port *ports __unused,
-                                            unsigned int *generation __unused)
+                                            struct audio_port *ports,
+                                            unsigned int *generation)
 {
-    *num_ports = 0;
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation);
 }
 
-status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused)
+status_t AudioPolicyService::getAudioPort(struct audio_port *port)
 {
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->getAudioPort(port);
 }
 
-status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused,
-        audio_patch_handle_t *handle __unused)
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch,
+        audio_patch_handle_t *handle)
 {
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+    return mAudioPolicyManager->createAudioPatch(patch, handle,
+                                                  IPCThreadState::self()->getCallingUid());
 }
 
-status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused)
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle)
 {
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->releaseAudioPatch(handle,
+                                                     IPCThreadState::self()->getCallingUid());
 }
 
 status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
-        struct audio_patch *patches __unused,
-        unsigned int *generation __unused)
+        struct audio_patch *patches,
+        unsigned int *generation)
 {
-    *num_patches = 0;
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation);
 }
 
-status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused)
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config)
 {
-    return INVALID_OPERATION;
+    Mutex::Autolock _l(mLock);
+    if (mAudioPolicyManager == NULL) {
+        return NO_INIT;
+    }
+
+    return mAudioPolicyManager->setAudioPortConfig(config);
 }
 
 }; // namespace android
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index db0f57d..bf5b9a8 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -38,9 +38,9 @@
 #include <utils/Log.h>
 #include <hardware/audio.h>
 #include <hardware/audio_effect.h>
-#include <hardware_legacy/audio_policy_conf.h>
 #include <media/AudioParameter.h>
 #include "AudioPolicyManager.h"
+#include "audio_policy_conf.h"
 
 namespace android {
 
@@ -137,6 +137,12 @@
     STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
 };
 
+const StringToEnum sGainModeNameToEnumTable[] = {
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
+    STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP),
+};
+
 
 uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table,
                                               size_t size,
@@ -189,9 +195,8 @@
     if (audio_is_output_device(device)) {
         SortedVector <audio_io_handle_t> outputs;
 
-        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                            address,
-                                                            0);
+        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+        devDesc->mAddress = address;
         ssize_t index = mAvailableOutputDevices.indexOf(devDesc);
 
         // save a copy of the opened output descriptors before any output is opened or closed
@@ -210,12 +215,19 @@
             if (checkOutputsForDevice(device, state, outputs, address) != NO_ERROR) {
                 return INVALID_OPERATION;
             }
+            // outputs should never be empty here
+            ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
+                    "checkOutputsForDevice() returned no outputs but status OK");
             ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
                   outputs.size());
             // register new device as available
             index = mAvailableOutputDevices.add(devDesc);
             if (index >= 0) {
                 mAvailableOutputDevices[index]->mId = nextUniqueId();
+                HwModule *module = getModuleForDevice(device);
+                ALOG_ASSERT(module != NULL, "setDeviceConnectionState():"
+                        "could not find HW module for device %08x", device);
+                mAvailableOutputDevices[index]->mModule = module;
             } else {
                 return NO_MEMORY;
             }
@@ -273,25 +285,16 @@
                             0);
         }
 
-        if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-            device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-        } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO ||
-                   device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
-                   device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
-            device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
-        } else {
-            return NO_ERROR;
-        }
+        mpClientInterface->onAudioPortListUpdate();
+        return NO_ERROR;
     }  // end if is output device
 
     // handle input devices
     if (audio_is_input_device(device)) {
         SortedVector <audio_io_handle_t> inputs;
 
-        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                            address,
-                                                            0);
-
+        sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+        devDesc->mAddress = address;
         ssize_t index = mAvailableInputDevices.indexOf(devDesc);
         switch (state)
         {
@@ -301,6 +304,12 @@
                 ALOGW("setDeviceConnectionState() device already connected: %d", device);
                 return INVALID_OPERATION;
             }
+            HwModule *module = getModuleForDevice(device);
+            if (module == NULL) {
+                ALOGW("setDeviceConnectionState(): could not find HW module for device %08x",
+                      device);
+                return INVALID_OPERATION;
+            }
             if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) {
                 return INVALID_OPERATION;
             }
@@ -308,6 +317,7 @@
             index = mAvailableInputDevices.add(devDesc);
             if (index >= 0) {
                 mAvailableInputDevices[index]->mId = nextUniqueId();
+                mAvailableInputDevices[index]->mModule = module;
             } else {
                 return NO_MEMORY;
             }
@@ -330,6 +340,7 @@
 
         closeAllInputs();
 
+        mpClientInterface->onAudioPortListUpdate();
         return NO_ERROR;
     } // end if is input device
 
@@ -342,9 +353,8 @@
 {
     audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
     String8 address = String8(device_address);
-    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
-                                                        String8(device_address),
-                                                        0);
+    sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+    devDesc->mAddress = String8(device_address);
     ssize_t index;
     DeviceVector *deviceVector;
 
@@ -741,6 +751,7 @@
         }
         mPreviousOutputs = mOutputs;
         ALOGV("getOutput() returns new direct output %d", output);
+        mpClientInterface->onAudioPortListUpdate();
         return output;
     }
 
@@ -973,6 +984,7 @@
             if (dstOutput != mPrimaryOutput) {
                 mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
             }
+            mpClientInterface->onAudioPortListUpdate();
         }
     }
 }
@@ -1054,6 +1066,7 @@
         return 0;
     }
     addInput(input, inputDesc);
+    mpClientInterface->onAudioPortListUpdate();
     return input;
 }
 
@@ -1138,6 +1151,8 @@
     mpClientInterface->closeInput(input);
     delete mInputs.valueAt(index);
     mInputs.removeItem(input);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPortListUpdate();
     ALOGV("releaseInput() exit");
 }
 
@@ -1146,6 +1161,7 @@
         mpClientInterface->closeInput(mInputs.keyAt(input_index));
     }
     mInputs.clear();
+    nextAudioPortGeneration();
 }
 
 void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream,
@@ -1466,15 +1482,13 @@
     snprintf(buffer, SIZE, " Available output devices:\n");
     result.append(buffer);
     write(fd, result.string(), result.size());
-    DeviceDescriptor::dumpHeader(fd, 2);
     for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
-        mAvailableOutputDevices[i]->dump(fd, 2);
+        mAvailableOutputDevices[i]->dump(fd, 2, i);
     }
     snprintf(buffer, SIZE, "\n Available input devices:\n");
     write(fd, buffer, strlen(buffer));
-    DeviceDescriptor::dumpHeader(fd, 2);
     for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
-        mAvailableInputDevices[i]->dump(fd, 2);
+        mAvailableInputDevices[i]->dump(fd, 2, i);
     }
 
     snprintf(buffer, SIZE, "\nHW Modules dump:\n");
@@ -1595,6 +1609,549 @@
     return (profile != 0);
 }
 
+status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
+                                            audio_port_type_t type,
+                                            unsigned int *num_ports,
+                                            struct audio_port *ports,
+                                            unsigned int *generation)
+{
+    if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+            generation == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
+    if (ports == NULL) {
+        *num_ports = 0;
+    }
+
+    size_t portsWritten = 0;
+    size_t portsMax = *num_ports;
+    *num_ports = 0;
+    if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_DEVICE) {
+        if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0;
+                    i  < mAvailableOutputDevices.size() && portsWritten < portsMax; i++) {
+                mAvailableOutputDevices[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mAvailableOutputDevices.size();
+        }
+        if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0;
+                    i  < mAvailableInputDevices.size() && portsWritten < portsMax; i++) {
+                mAvailableInputDevices[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mAvailableInputDevices.size();
+        }
+    }
+    if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_MIX) {
+        if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0; i < mInputs.size() && portsWritten < portsMax; i++) {
+                mInputs[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mInputs.size();
+        }
+        if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+            for (size_t i = 0; i < mOutputs.size() && portsWritten < portsMax; i++) {
+                mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+            }
+            *num_ports += mOutputs.size();
+        }
+    }
+    *generation = curAudioPortGeneration();
+    ALOGV("listAudioPorts() got %d ports needed %d", portsWritten, *num_ports);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused)
+{
+    return NO_ERROR;
+}
+
+AudioPolicyManager::AudioOutputDescriptor *AudioPolicyManager::getOutputFromId(
+                                                                    audio_port_handle_t id) const
+{
+    AudioOutputDescriptor *outputDesc = NULL;
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        outputDesc = mOutputs.valueAt(i);
+        if (outputDesc->mId == id) {
+            break;
+        }
+    }
+    return outputDesc;
+}
+
+AudioPolicyManager::AudioInputDescriptor *AudioPolicyManager::getInputFromId(
+                                                                    audio_port_handle_t id) const
+{
+    AudioInputDescriptor *inputDesc = NULL;
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        inputDesc = mInputs.valueAt(i);
+        if (inputDesc->mId == id) {
+            break;
+        }
+    }
+    return inputDesc;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleForDevice(audio_devices_t device) const
+{
+    for (size_t i = 0; i < mHwModules.size(); i++) {
+        if (mHwModules[i]->mHandle == 0) {
+            continue;
+        }
+        if (audio_is_output_device(device)) {
+            for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
+            {
+                if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) {
+                    return mHwModules[i];
+                }
+            }
+        } else {
+            for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) {
+                if (mHwModules[i]->mInputProfiles[j]->mSupportedDevices.types() &
+                        device & ~AUDIO_DEVICE_BIT_IN) {
+                    return mHwModules[i];
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleFromName(const char *name) const
+{
+    for (size_t i = 0; i < mHwModules.size(); i++)
+    {
+        if (strcmp(mHwModules[i]->mName, name) == 0) {
+            return mHwModules[i];
+        }
+    }
+    return NULL;
+}
+
+
+status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
+                                               audio_patch_handle_t *handle,
+                                               uid_t uid)
+{
+    ALOGV("createAudioPatch()");
+
+    if (handle == NULL || patch == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
+
+    if (patch->num_sources > 1 || patch->num_sinks > 1) {
+        return INVALID_OPERATION;
+    }
+    if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
+            patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+        return INVALID_OPERATION;
+    }
+
+    sp<AudioPatch> patchDesc;
+    ssize_t index = mAudioPatches.indexOfKey(*handle);
+
+    ALOGV("createAudioPatch sink id %d role %d type %d", patch->sinks[0].id, patch->sinks[0].role,
+                                                         patch->sinks[0].type);
+    ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
+                                                           patch->sources[0].role,
+                                                           patch->sources[0].type);
+
+    if (index >= 0) {
+        patchDesc = mAudioPatches.valueAt(index);
+        ALOGV("createAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+                                                                  mUidCached, patchDesc->mUid, uid);
+        if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+            return INVALID_OPERATION;
+        }
+    } else {
+        *handle = 0;
+    }
+
+    if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+        // TODO add support for mix to mix connection
+        if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
+            ALOGV("createAudioPatch() source mix sink not device");
+            return BAD_VALUE;
+        }
+        // output mix to output device connection
+        AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+        if (outputDesc == NULL) {
+            ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
+            return BAD_VALUE;
+        }
+        if (patchDesc != 0) {
+            if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
+                ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
+                                          patchDesc->mPatch.sources[0].id, patch->sources[0].id);
+                return BAD_VALUE;
+            }
+        }
+        sp<DeviceDescriptor> devDesc =
+                mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+        if (devDesc == 0) {
+            ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[0].id);
+            return BAD_VALUE;
+        }
+
+        if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+                                                       patch->sources[0].sample_rate,
+                                                     patch->sources[0].format,
+                                                     patch->sources[0].channel_mask,
+                                                     AUDIO_OUTPUT_FLAG_NONE)) {
+            return INVALID_OPERATION;
+        }
+        // TODO: reconfigure output format and channels here
+        ALOGV("createAudioPatch() setting device %08x on output %d",
+                                              devDesc->mType, outputDesc->mIoHandle);
+        setOutputDevice(outputDesc->mIoHandle,
+                        devDesc->mType,
+                       true,
+                       0,
+                       handle);
+        index = mAudioPatches.indexOfKey(*handle);
+        if (index >= 0) {
+            if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+                ALOGW("createAudioPatch() setOutputDevice() did not reuse the patch provided");
+            }
+            patchDesc = mAudioPatches.valueAt(index);
+            patchDesc->mUid = uid;
+            ALOGV("createAudioPatch() success");
+        } else {
+            ALOGW("createAudioPatch() setOutputDevice() failed to create a patch");
+            return INVALID_OPERATION;
+        }
+    } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+            // input device to input mix connection
+            AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+            if (inputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            if (patchDesc != 0) {
+                if (patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+                    return BAD_VALUE;
+                }
+            }
+            sp<DeviceDescriptor> devDesc =
+                    mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+            if (devDesc == 0) {
+                return BAD_VALUE;
+            }
+
+            if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+                                                           patch->sinks[0].sample_rate,
+                                                         patch->sinks[0].format,
+                                                         patch->sinks[0].channel_mask,
+                                                         AUDIO_OUTPUT_FLAG_NONE)) {
+                return INVALID_OPERATION;
+            }
+            // TODO: reconfigure output format and channels here
+            ALOGV("createAudioPatch() setting device %08x on output %d",
+                                                  devDesc->mType, inputDesc->mIoHandle);
+            setInputDevice(inputDesc->mIoHandle,
+                           devDesc->mType,
+                           true,
+                           handle);
+            index = mAudioPatches.indexOfKey(*handle);
+            if (index >= 0) {
+                if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+                    ALOGW("createAudioPatch() setInputDevice() did not reuse the patch provided");
+                }
+                patchDesc = mAudioPatches.valueAt(index);
+                patchDesc->mUid = uid;
+                ALOGV("createAudioPatch() success");
+            } else {
+                ALOGW("createAudioPatch() setInputDevice() failed to create a patch");
+                return INVALID_OPERATION;
+            }
+        } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+            // device to device connection
+            if (patchDesc != 0) {
+                if (patchDesc->mPatch.sources[0].id != patch->sources[0].id &&
+                    patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+                    return BAD_VALUE;
+                }
+            }
+
+            sp<DeviceDescriptor> srcDeviceDesc =
+                    mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+            sp<DeviceDescriptor> sinkDeviceDesc =
+                    mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+            if (srcDeviceDesc == 0 || sinkDeviceDesc == 0) {
+                return BAD_VALUE;
+            }
+            //update source and sink with our own data as the data passed in the patch may
+            // be incomplete.
+            struct audio_patch newPatch = *patch;
+            srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
+            sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]);
+
+            // TODO: add support for devices on different HW modules
+            if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+                return INVALID_OPERATION;
+            }
+            // TODO: check from routing capabilities in config file and other conflicting patches
+
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
+            status_t status = mpClientInterface->createAudioPatch(&newPatch,
+                                                                  &afPatchHandle,
+                                                                  0);
+            ALOGV("createAudioPatch() patch panel returned %d patchHandle %d",
+                                                                  status, afPatchHandle);
+            if (status == NO_ERROR) {
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &newPatch, uid);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = newPatch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                *handle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
+            } else {
+                ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
+                status);
+                return INVALID_OPERATION;
+            }
+        } else {
+            return BAD_VALUE;
+        }
+    } else {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle,
+                                                  uid_t uid)
+{
+    ALOGV("releaseAudioPatch() patch %d", handle);
+
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index < 0) {
+        return BAD_VALUE;
+    }
+    sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    ALOGV("releaseAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+          mUidCached, patchDesc->mUid, uid);
+    if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+        return INVALID_OPERATION;
+    }
+
+    struct audio_patch *patch = &patchDesc->mPatch;
+    patchDesc->mUid = mUidCached;
+    if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+        AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+        if (outputDesc == NULL) {
+            ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
+            return BAD_VALUE;
+        }
+
+        setOutputDevice(outputDesc->mIoHandle,
+                        getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+                       true,
+                       0,
+                       NULL);
+    } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+        if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+            AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+            if (inputDesc == NULL) {
+                ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id);
+                return BAD_VALUE;
+            }
+            setInputDevice(inputDesc->mIoHandle,
+                           getNewInputDevice(inputDesc->mIoHandle),
+                           true,
+                           NULL);
+        } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+            audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
+            status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+            ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
+                                                              status, patchDesc->mAfPatchHandle);
+            removeAudioPatch(patchDesc->mHandle);
+            nextAudioPortGeneration();
+            mpClientInterface->onAudioPatchListUpdate();
+        } else {
+            return BAD_VALUE;
+        }
+    } else {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
+                                              struct audio_patch *patches,
+                                              unsigned int *generation)
+{
+    if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+            generation == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("listAudioPatches() num_patches %d patches %p available patches %d",
+          *num_patches, patches, mAudioPatches.size());
+    if (patches == NULL) {
+        *num_patches = 0;
+    }
+
+    size_t patchesWritten = 0;
+    size_t patchesMax = *num_patches;
+    for (size_t i = 0;
+            i  < mAudioPatches.size() && patchesWritten < patchesMax; i++) {
+        patches[patchesWritten] = mAudioPatches[i]->mPatch;
+        patches[patchesWritten++].id = mAudioPatches[i]->mHandle;
+        ALOGV("listAudioPatches() patch %d num_sources %d num_sinks %d",
+              i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks);
+    }
+    *num_patches = mAudioPatches.size();
+
+    *generation = curAudioPortGeneration();
+    ALOGV("listAudioPatches() got %d patches needed %d", patchesWritten, *num_patches);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config)
+{
+    ALOGV("setAudioPortConfig()");
+
+    if (config == NULL) {
+        return BAD_VALUE;
+    }
+    ALOGV("setAudioPortConfig() on port handle %d", config->id);
+    // Only support gain configuration for now
+    if (config->config_mask != AUDIO_PORT_CONFIG_GAIN || config->gain.index < 0) {
+        return BAD_VALUE;
+    }
+
+    sp<AudioPort> portDesc;
+    struct audio_port_config portConfig;
+    if (config->type == AUDIO_PORT_TYPE_MIX) {
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            AudioOutputDescriptor *outputDesc = getOutputFromId(config->id);
+            if (outputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = outputDesc->mProfile;
+            outputDesc->toAudioPortConfig(&portConfig);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            AudioInputDescriptor *inputDesc = getInputFromId(config->id);
+            if (inputDesc == NULL) {
+                return BAD_VALUE;
+            }
+            portDesc = inputDesc->mProfile;
+            inputDesc->toAudioPortConfig(&portConfig);
+        } else {
+            return BAD_VALUE;
+        }
+    } else if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+        sp<DeviceDescriptor> deviceDesc;
+        if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+            deviceDesc = mAvailableInputDevices.getDeviceFromId(config->id);
+        } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+            deviceDesc = mAvailableOutputDevices.getDeviceFromId(config->id);
+        } else {
+            return BAD_VALUE;
+        }
+        if (deviceDesc == NULL) {
+            return BAD_VALUE;
+        }
+        portDesc = deviceDesc;
+        deviceDesc->toAudioPortConfig(&portConfig);
+    } else {
+        return BAD_VALUE;
+    }
+
+    if ((size_t)config->gain.index >= portDesc->mGains.size()) {
+        return INVALID_OPERATION;
+    }
+    const struct audio_gain *gain = &portDesc->mGains[config->gain.index]->mGain;
+    if ((config->gain.mode & ~gain->mode) != 0) {
+        return BAD_VALUE;
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+        if ((config->gain.values[0] < gain->min_value) ||
+                    (config->gain.values[0] > gain->max_value)) {
+            return BAD_VALUE;
+        }
+    } else {
+        if ((config->gain.channel_mask & ~gain->channel_mask) != 0) {
+            return BAD_VALUE;
+        }
+        size_t numValues = popcount(config->gain.channel_mask);
+        for (size_t i = 0; i < numValues; i++) {
+            if ((config->gain.values[i] < gain->min_value) ||
+                    (config->gain.values[i] > gain->max_value)) {
+                return BAD_VALUE;
+            }
+        }
+    }
+    if ((config->gain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+        if ((config->gain.ramp_duration_ms < gain->min_ramp_ms) ||
+                    (config->gain.ramp_duration_ms > gain->max_ramp_ms)) {
+            return BAD_VALUE;
+        }
+    }
+
+    portConfig.gain = config->gain;
+
+    status_t status = mpClientInterface->setAudioPortConfig(&portConfig, 0);
+
+    return status;
+}
+
+void AudioPolicyManager::clearAudioPatches(uid_t uid)
+{
+    for (ssize_t i = 0; i < (ssize_t)mAudioPatches.size(); i++)  {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i);
+        if (patchDesc->mUid == uid) {
+            // releaseAudioPatch() removes the patch from mAudioPatches
+            if (releaseAudioPatch(mAudioPatches.keyAt(i), uid) == NO_ERROR) {
+                i--;
+            }
+        }
+    }
+}
+
+status_t AudioPolicyManager::addAudioPatch(audio_patch_handle_t handle,
+                                           const sp<AudioPatch>& patch)
+{
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index >= 0) {
+        ALOGW("addAudioPatch() patch %d already in", handle);
+        return ALREADY_EXISTS;
+    }
+    mAudioPatches.add(handle, patch);
+    ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d"
+            "sink handle %d",
+          handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks,
+          patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id);
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeAudioPatch(audio_patch_handle_t handle)
+{
+    ssize_t index = mAudioPatches.indexOfKey(handle);
+
+    if (index < 0) {
+        ALOGW("removeAudioPatch() patch %d not in", handle);
+        return ALREADY_EXISTS;
+    }
+    ALOGV("removeAudioPatch() handle %d af handle %d", handle,
+                      mAudioPatches.valueAt(index)->mAfPatchHandle);
+    mAudioPatches.removeItemsAt(index);
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 // AudioPolicyManager
 // ----------------------------------------------------------------------------
@@ -1604,6 +2161,11 @@
     return android_atomic_inc(&mNextUniqueId);
 }
 
+uint32_t AudioPolicyManager::nextAudioPortGeneration()
+{
+    return android_atomic_inc(&mAudioPortGeneration);
+}
+
 AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
     :
 #ifdef AUDIO_POLICY_TEST
@@ -1614,15 +2176,17 @@
     mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
     mA2dpSuspended(false),
-    mSpeakerDrcEnabled(false), mNextUniqueId(0)
+    mSpeakerDrcEnabled(false), mNextUniqueId(1),
+    mAudioPortGeneration(1)
 {
+    mUidCached = getuid();
     mpClientInterface = clientInterface;
 
     for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) {
         mForceUse[i] = AUDIO_POLICY_FORCE_NONE;
     }
 
-    mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
+    mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER);
     if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) {
         if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) {
             ALOGE("could not load audio policy configuration file, setting defaults");
@@ -1683,6 +2247,7 @@
                         // give a valid ID to an attached device once confirmed it is reachable
                         if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
                             mAvailableOutputDevices[index]->mId = nextUniqueId();
+                            mAvailableOutputDevices[index]->mModule = mHwModules[i];
                         }
                     }
                     if (mPrimaryOutput == 0 &&
@@ -1690,6 +2255,7 @@
                         mPrimaryOutput = output;
                     }
                     addOutput(output, outputDesc);
+                    ALOGI("CSTOR setOutputDevice %08x", outputDesc->mDevice);
                     setOutputDevice(output,
                                     outputDesc->mDevice,
                                     true);
@@ -1728,6 +2294,7 @@
                         // give a valid ID to an attached device once confirmed it is reachable
                         if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
                             mAvailableInputDevices[index]->mId = nextUniqueId();
+                            mAvailableInputDevices[index]->mModule = mHwModules[i];
                         }
                     }
                     mpClientInterface->closeInput(input);
@@ -1973,6 +2540,7 @@
     outputDesc->mIoHandle = output;
     outputDesc->mId = nextUniqueId();
     mOutputs.add(output, outputDesc);
+    nextAudioPortGeneration();
 }
 
 void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc)
@@ -1980,6 +2548,7 @@
     inputDesc->mIoHandle = input;
     inputDesc->mId = nextUniqueId();
     mInputs.add(input, inputDesc);
+    nextAudioPortGeneration();
 }
 
 String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address)
@@ -2152,6 +2721,7 @@
                                     mPrimaryOutput, output);
                             mpClientInterface->closeOutput(output);
                             mOutputs.removeItem(output);
+                            nextAudioPortGeneration();
                             output = 0;
                         }
                     }
@@ -2438,6 +3008,7 @@
     delete outputDesc;
     mOutputs.removeItem(output);
     mPreviousOutputs = mOutputs;
+    nextAudioPortGeneration();
 }
 
 SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
@@ -2590,6 +3161,17 @@
     audio_devices_t device = AUDIO_DEVICE_NONE;
 
     AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+    ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+    if (index >= 0) {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+        if (patchDesc->mUid != mUidCached) {
+            ALOGV("getNewOutputDevice() device %08x forced by patch %d",
+                  outputDesc->device(), outputDesc->mPatchHandle);
+            return outputDesc->device();
+        }
+    }
+
     // check the following by order of priority to request a routing change if necessary:
     // 1: the strategy enforced audible is active on the output:
     //      use device for strategy enforced audible
@@ -2625,6 +3207,17 @@
 audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input)
 {
     AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
+
+    ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+    if (index >= 0) {
+        sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+        if (patchDesc->mUid != mUidCached) {
+            ALOGV("getNewInputDevice() device %08x forced by patch %d",
+                  inputDesc->mDevice, inputDesc->mPatchHandle);
+            return inputDesc->mDevice;
+        }
+    }
+
     audio_devices_t device = getDeviceForInputSource(inputDesc->mInputSource);
 
     ALOGV("getNewInputDevice() selected device %x", device);
@@ -2636,15 +3229,22 @@
 }
 
 audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) {
-    audio_devices_t devices;
     // By checking the range of stream before calling getStrategy, we avoid
     // getStrategy's behavior for invalid streams.  getStrategy would do a ALOGE
     // and then return STRATEGY_MEDIA, but we want to return the empty set.
     if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_CNT) {
-        devices = AUDIO_DEVICE_NONE;
-    } else {
-        AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
-        devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+        return AUDIO_DEVICE_NONE;
+    }
+    audio_devices_t devices;
+    AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
+    devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+    SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs);
+    for (size_t i = 0; i < outputs.size(); i++) {
+        AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]);
+        if (outputDesc->isStrategyActive(strategy)) {
+            devices = outputDesc->device();
+            break;
+        }
     }
     return devices;
 }
@@ -2990,7 +3590,8 @@
 uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
                                              audio_devices_t device,
                                              bool force,
-                                             int delayMs)
+                                             int delayMs,
+                                             audio_patch_handle_t *patchHandle)
 {
     ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
     AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
@@ -3034,7 +3635,7 @@
 
     // do the routing
     if (device == AUDIO_DEVICE_NONE) {
-        resetOutputDevice(output, delayMs);
+        resetOutputDevice(output, delayMs, NULL);
     } else {
         DeviceVector deviceList = mAvailableOutputDevices.getDevicesFromType(device);
         if (!deviceList.isEmpty()) {
@@ -3044,18 +3645,43 @@
             patch.num_sinks = 0;
             for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {
                 deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);
-                patch.sinks[i].ext.device.hw_module = patch.sources[0].ext.mix.hw_module;
                 patch.num_sinks++;
             }
-            audio_patch_handle_t patchHandle = outputDesc->mPatchHandle;
+            ssize_t index;
+            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+                index = mAudioPatches.indexOfKey(*patchHandle);
+            } else {
+                index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+            }
+            sp< AudioPatch> patchDesc;
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                patchDesc = mAudioPatches.valueAt(index);
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
             status_t status = mpClientInterface->createAudioPatch(&patch,
-                                                                  &patchHandle,
-                                                                  delayMs);
+                                                                   &afPatchHandle,
+                                                                   delayMs);
             ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d"
                     "num_sources %d num_sinks %d",
-                                       status, patchHandle, patch.num_sources, patch.num_sinks);
+                                       status, afPatchHandle, patch.num_sources, patch.num_sinks);
             if (status == NO_ERROR) {
-                outputDesc->mPatchHandle = patchHandle;
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &patch, mUidCached);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = patch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                patchDesc->mUid = mUidCached;
+                if (patchHandle) {
+                    *patchHandle = patchDesc->mHandle;
+                }
+                outputDesc->mPatchHandle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
             }
         }
     }
@@ -3067,21 +3693,33 @@
 }
 
 status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
-                                               int delayMs)
+                                               int delayMs,
+                                               audio_patch_handle_t *patchHandle)
 {
     AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-    if (outputDesc->mPatchHandle == 0) {
+    ssize_t index;
+    if (patchHandle) {
+        index = mAudioPatches.indexOfKey(*patchHandle);
+    } else {
+        index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+    }
+    if (index < 0) {
         return INVALID_OPERATION;
     }
-    status_t status = mpClientInterface->releaseAudioPatch(outputDesc->mPatchHandle, delayMs);
+    sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, delayMs);
     ALOGV("resetOutputDevice() releaseAudioPatch returned %d", status);
     outputDesc->mPatchHandle = 0;
+    removeAudioPatch(patchDesc->mHandle);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
     return status;
 }
 
 status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input,
                                             audio_devices_t device,
-                                            bool force)
+                                            bool force,
+                                            audio_patch_handle_t *patchHandle)
 {
     status_t status = NO_ERROR;
 
@@ -3096,31 +3734,67 @@
             patch.num_sinks = 1;
             //only one input device for now
             deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]);
-            patch.sources[0].ext.device.hw_module = patch.sinks[0].ext.mix.hw_module;
             patch.num_sources = 1;
-            audio_patch_handle_t patchHandle = inputDesc->mPatchHandle;
+            ssize_t index;
+            if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+                index = mAudioPatches.indexOfKey(*patchHandle);
+            } else {
+                index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+            }
+            sp< AudioPatch> patchDesc;
+            audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+            if (index >= 0) {
+                patchDesc = mAudioPatches.valueAt(index);
+                afPatchHandle = patchDesc->mAfPatchHandle;
+            }
+
             status_t status = mpClientInterface->createAudioPatch(&patch,
-                                                                  &patchHandle,
+                                                                  &afPatchHandle,
                                                                   0);
             ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d",
-                                                                          status, patchHandle);
+                                                                          status, afPatchHandle);
             if (status == NO_ERROR) {
-                inputDesc->mPatchHandle = patchHandle;
+                if (index < 0) {
+                    patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+                                               &patch, mUidCached);
+                    addAudioPatch(patchDesc->mHandle, patchDesc);
+                } else {
+                    patchDesc->mPatch = patch;
+                }
+                patchDesc->mAfPatchHandle = afPatchHandle;
+                patchDesc->mUid = mUidCached;
+                if (patchHandle) {
+                    *patchHandle = patchDesc->mHandle;
+                }
+                inputDesc->mPatchHandle = patchDesc->mHandle;
+                nextAudioPortGeneration();
+                mpClientInterface->onAudioPatchListUpdate();
             }
         }
     }
     return status;
 }
 
-status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input)
+status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
+                                              audio_patch_handle_t *patchHandle)
 {
     AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
-    if (inputDesc->mPatchHandle == 0) {
+    ssize_t index;
+    if (patchHandle) {
+        index = mAudioPatches.indexOfKey(*patchHandle);
+    } else {
+        index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+    }
+    if (index < 0) {
         return INVALID_OPERATION;
     }
-    status_t status = mpClientInterface->releaseAudioPatch(inputDesc->mPatchHandle, 0);
+    sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+    status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
     ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
     inputDesc->mPatchHandle = 0;
+    removeAudioPatch(patchDesc->mHandle);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPatchListUpdate();
     return status;
 }
 
@@ -3723,6 +4397,7 @@
     return MAX_EFFECTS_MEMORY;
 }
 
+
 // --- AudioOutputDescriptor class implementation
 
 AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor(
@@ -3849,20 +4524,37 @@
 }
 
 void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig(
-                                                        struct audio_port_config *config) const
+                                                 struct audio_port_config *dstConfig,
+                                                 const struct audio_port_config *srcConfig) const
 {
-    config->id = mId;
-    config->role = AUDIO_PORT_ROLE_SOURCE;
-    config->type = AUDIO_PORT_TYPE_MIX;
-    config->sample_rate = mSamplingRate;
-    config->channel_mask = mChannelMask;
-    config->format = mFormat;
-    config->gain.index = -1;
-    config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+    dstConfig->id = mId;
+    dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
+    dstConfig->type = AUDIO_PORT_TYPE_MIX;
+    dstConfig->sample_rate = mSamplingRate;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->format = mFormat;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
                             AUDIO_PORT_CONFIG_FORMAT;
-    config->ext.mix.hw_module = mProfile->mModule->mHandle;
-    config->ext.mix.handle = mIoHandle;
-    config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+            dstConfig->sample_rate = srcConfig->sample_rate;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+            dstConfig->format = srcConfig->format;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
+    dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+    dstConfig->ext.mix.handle = mIoHandle;
+    dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
 }
 
 void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
@@ -3870,6 +4562,8 @@
 {
     mProfile->toAudioPort(port);
     port->id = mId;
+    toAudioPortConfig(&port->active_config);
+    port->ext.mix.hw_module = mProfile->mModule->mHandle;
     port->ext.mix.handle = mIoHandle;
     port->ext.mix.latency_class =
             mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
@@ -3921,21 +4615,34 @@
 }
 
 void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig(
-                                                        struct audio_port_config *config) const
+                                                   struct audio_port_config *dstConfig,
+                                                   const struct audio_port_config *srcConfig) const
 {
-    config->id = mId;
-    config->role = AUDIO_PORT_ROLE_SINK;
-    config->type = AUDIO_PORT_TYPE_MIX;
-    config->sample_rate = mSamplingRate;
-    config->channel_mask = mChannelMask;
-    config->format = mFormat;
-    config->gain.index = -1;
-    config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
-                          AUDIO_PORT_CONFIG_FORMAT;
-    config->ext.mix.hw_module = mProfile->mModule->mHandle;
-    config->ext.mix.handle = mIoHandle;
-    config->ext.mix.usecase.source = (mInputSource == AUDIO_SOURCE_HOTWORD) ?
-                                                    AUDIO_SOURCE_VOICE_RECOGNITION : mInputSource;
+    dstConfig->id = mId;
+    dstConfig->role = AUDIO_PORT_ROLE_SINK;
+    dstConfig->type = AUDIO_PORT_TYPE_MIX;
+    dstConfig->sample_rate = mSamplingRate;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->format = mFormat;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+                            AUDIO_PORT_CONFIG_FORMAT;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+            dstConfig->sample_rate = srcConfig->sample_rate;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+            dstConfig->format = srcConfig->format;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
 }
 
 void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
@@ -3943,6 +4650,8 @@
 {
     mProfile->toAudioPort(port);
     port->id = mId;
+    toAudioPortConfig(&port->active_config);
+    port->ext.mix.hw_module = mProfile->mModule->mHandle;
     port->ext.mix.handle = mIoHandle;
     port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
 }
@@ -4047,6 +4756,140 @@
     free((void *)mName);
 }
 
+status_t AudioPolicyManager::HwModule::loadInput(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+
+    while (node) {
+        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+            profile->loadSamplingRates((char *)node->value);
+        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+            profile->loadFormats((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            profile->loadInChannels((char *)node->value);
+        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+                                                           mDeclaredDevices);
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            profile->loadGains(node);
+        }
+        node = node->next;
+    }
+    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+            "loadInput() invalid supported devices");
+    ALOGW_IF(profile->mChannelMasks.size() == 0,
+            "loadInput() invalid supported channel masks");
+    ALOGW_IF(profile->mSamplingRates.size() == 0,
+            "loadInput() invalid supported sampling rates");
+    ALOGW_IF(profile->mFormats.size() == 0,
+            "loadInput() invalid supported formats");
+    if (!profile->mSupportedDevices.isEmpty() &&
+            (profile->mChannelMasks.size() != 0) &&
+            (profile->mSamplingRates.size() != 0) &&
+            (profile->mFormats.size() != 0)) {
+
+        ALOGV("loadInput() adding input Supported Devices %04x",
+              profile->mSupportedDevices.types());
+
+        mInputProfiles.add(profile);
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+status_t AudioPolicyManager::HwModule::loadOutput(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+
+    while (node) {
+        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+            profile->loadSamplingRates((char *)node->value);
+        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+            profile->loadFormats((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            profile->loadOutChannels((char *)node->value);
+        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+            profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+                                                           mDeclaredDevices);
+        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
+            profile->mFlags = parseFlagNames((char *)node->value);
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            profile->loadGains(node);
+        }
+        node = node->next;
+    }
+    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+            "loadOutput() invalid supported devices");
+    ALOGW_IF(profile->mChannelMasks.size() == 0,
+            "loadOutput() invalid supported channel masks");
+    ALOGW_IF(profile->mSamplingRates.size() == 0,
+            "loadOutput() invalid supported sampling rates");
+    ALOGW_IF(profile->mFormats.size() == 0,
+            "loadOutput() invalid supported formats");
+    if (!profile->mSupportedDevices.isEmpty() &&
+            (profile->mChannelMasks.size() != 0) &&
+            (profile->mSamplingRates.size() != 0) &&
+            (profile->mFormats.size() != 0)) {
+
+        ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
+              profile->mSupportedDevices.types(), profile->mFlags);
+
+        mOutputProfiles.add(profile);
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+status_t AudioPolicyManager::HwModule::loadDevice(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    audio_devices_t type = AUDIO_DEVICE_NONE;
+    while (node) {
+        if (strcmp(node->name, DEVICE_TYPE) == 0) {
+            type = parseDeviceNames((char *)node->value);
+            break;
+        }
+        node = node->next;
+    }
+    if (type == AUDIO_DEVICE_NONE ||
+            (!audio_is_input_device(type) && !audio_is_output_device(type))) {
+        ALOGW("loadDevice() bad type %08x", type);
+        return BAD_VALUE;
+    }
+    sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
+    deviceDesc->mModule = this;
+
+    node = root->first_child;
+    while (node) {
+        if (strcmp(node->name, DEVICE_ADDRESS) == 0) {
+            deviceDesc->mAddress = String8((char *)node->value);
+        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+            if (audio_is_input_device(type)) {
+                deviceDesc->loadInChannels((char *)node->value);
+            } else {
+                deviceDesc->loadOutChannels((char *)node->value);
+            }
+        } else if (strcmp(node->name, GAINS_TAG) == 0) {
+            deviceDesc->loadGains(node);
+        }
+        node = node->next;
+    }
+
+    ALOGV("loadDevice() adding device name %s type %08x address %s",
+          deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+
+    mDeclaredDevices.add(deviceDesc);
+
+    return NO_ERROR;
+}
+
 void AudioPolicyManager::HwModule::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -4074,6 +4917,12 @@
             mInputProfiles[i]->dump(fd);
         }
     }
+    if (mDeclaredDevices.size()) {
+        write(fd, "  - devices:\n", strlen("  - devices:\n"));
+        for (size_t i = 0; i < mDeclaredDevices.size(); i++) {
+            mDeclaredDevices[i]->dump(fd, 4, i);
+        }
+    }
 }
 
 // --- AudioPort class implementation
@@ -4095,7 +4944,13 @@
         port->formats[i] = mFormats[i];
     }
     port->num_formats = i;
-    port->num_gains = 0;
+
+    ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+
+    for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
+        port->gains[i] = mGains[i]->mGain;
+    }
+    port->num_gains = i;
 }
 
 
@@ -4118,7 +4973,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadFormats(char *name)
@@ -4141,7 +4995,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadInChannels(char *name)
@@ -4166,7 +5019,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
@@ -4195,10 +5047,174 @@
     return;
 }
 
+audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name)
+{
+    const char *str = strtok(name, "|");
+
+    ALOGV("loadGainMode() %s", name);
+    audio_gain_mode_t mode = 0;
+    while (str != NULL) {
+        mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable,
+                                                ARRAY_SIZE(sGainModeNameToEnumTable),
+                                                str);
+        str = strtok(NULL, "|");
+    }
+    return mode;
+}
+
+void AudioPolicyManager::AudioPort::loadGain(cnode *root)
+{
+    cnode *node = root->first_child;
+
+    sp<AudioGain> gain = new AudioGain();
+
+    while (node) {
+        if (strcmp(node->name, GAIN_MODE) == 0) {
+            gain->mGain.mode = loadGainMode((char *)node->value);
+        } else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
+            if ((mType == AUDIO_PORT_TYPE_DEVICE && mRole == AUDIO_PORT_ROLE_SOURCE) ||
+                    (mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK)) {
+                gain->mGain.channel_mask =
+                        (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+                                                           ARRAY_SIZE(sInChannelsNameToEnumTable),
+                                                           (char *)node->value);
+            } else {
+                gain->mGain.channel_mask =
+                        (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+                                                           ARRAY_SIZE(sOutChannelsNameToEnumTable),
+                                                           (char *)node->value);
+            }
+        } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
+            gain->mGain.min_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
+            gain->mGain.max_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
+            gain->mGain.default_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
+            gain->mGain.step_value = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
+            gain->mGain.min_ramp_ms = atoi((char *)node->value);
+        } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
+            gain->mGain.max_ramp_ms = atoi((char *)node->value);
+        }
+        node = node->next;
+    }
+
+    ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
+          gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value);
+
+    if (gain->mGain.mode == 0) {
+        return;
+    }
+    mGains.add(gain);
+}
+
+void AudioPolicyManager::AudioPort::loadGains(cnode *root)
+{
+    cnode *node = root->first_child;
+    while (node) {
+        ALOGV("loadGains() loading gain %s", node->name);
+        loadGain(node);
+        node = node->next;
+    }
+}
+
+void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    if (mName.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
+        result.append(buffer);
+    }
+
+    if (mSamplingRates.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mSamplingRates.size(); i++) {
+            snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
+            result.append(buffer);
+            result.append(i == (mSamplingRates.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+
+    if (mChannelMasks.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mChannelMasks.size(); i++) {
+            snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
+            result.append(buffer);
+            result.append(i == (mChannelMasks.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+
+    if (mFormats.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- formats: ", spaces, "");
+        result.append(buffer);
+        for (size_t i = 0; i < mFormats.size(); i++) {
+            snprintf(buffer, SIZE, "%-48s", enumToString(sFormatNameToEnumTable,
+                                                          ARRAY_SIZE(sFormatNameToEnumTable),
+                                                          mFormats[i]));
+            result.append(buffer);
+            result.append(i == (mFormats.size() - 1) ? "" : ", ");
+        }
+        result.append("\n");
+    }
+    write(fd, result.string(), result.size());
+    if (mGains.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
+        write(fd, buffer, strlen(buffer) + 1);
+        result.append(buffer);
+        for (size_t i = 0; i < mGains.size(); i++) {
+            mGains[i]->dump(fd, spaces + 2, i);
+        }
+    }
+}
+
+// --- AudioGain class implementation
+
+AudioPolicyManager::AudioGain::AudioGain()
+{
+    memset(&mGain, 0, sizeof(struct audio_gain));
+}
+
+void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms);
+    result.append(buffer);
+    snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms);
+    result.append(buffer);
+
+    write(fd, result.string(), result.size());
+}
+
 // --- IOProfile class implementation
 
-AudioPolicyManager::IOProfile::IOProfile(audio_port_role_t role, HwModule *module)
-    : AudioPort(AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
+AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role,
+                                         HwModule *module)
+    : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
 {
 }
 
@@ -4262,42 +5278,16 @@
     char buffer[SIZE];
     String8 result;
 
-    snprintf(buffer, SIZE, "    - sampling rates: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mSamplingRates.size(); i++) {
-        snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
-        result.append(buffer);
-        result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - channel masks: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mChannelMasks.size(); i++) {
-        snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
-        result.append(buffer);
-        result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - formats: ");
-    result.append(buffer);
-    for (size_t i = 0; i < mFormats.size(); i++) {
-        snprintf(buffer, SIZE, "0x%08x", mFormats[i]);
-        result.append(buffer);
-        result.append(i == (mFormats.size() - 1) ? "\n" : ", ");
-    }
-
-    snprintf(buffer, SIZE, "    - devices:\n");
-    result.append(buffer);
-    write(fd, result.string(), result.size());
-    DeviceDescriptor::dumpHeader(fd, 6);
-    for (size_t i = 0; i < mSupportedDevices.size(); i++) {
-        mSupportedDevices[i]->dump(fd, 6);
-    }
+    AudioPort::dump(fd, 4);
 
     snprintf(buffer, SIZE, "    - flags: 0x%04x\n", mFlags);
     result.append(buffer);
-
+    snprintf(buffer, SIZE, "    - devices:\n");
+    result.append(buffer);
     write(fd, result.string(), result.size());
+    for (size_t i = 0; i < mSupportedDevices.size(); i++) {
+        mSupportedDevices[i]->dump(fd, 6, i);
+    }
 }
 
 void AudioPolicyManager::IOProfile::log()
@@ -4402,10 +5392,33 @@
         uint32_t i = 31 - __builtin_clz(types);
         uint32_t type = 1 << i;
         types &= ~type;
-        add(new DeviceDescriptor(type | role_bit));
+        add(new DeviceDescriptor(String8(""), type | role_bit));
     }
 }
 
+void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name,
+                                                           const DeviceVector& declaredDevices)
+{
+    char *devName = strtok(name, "|");
+    while (devName != NULL) {
+        if (strlen(devName) != 0) {
+            audio_devices_t type = stringToEnum(sDeviceNameToEnumTable,
+                                 ARRAY_SIZE(sDeviceNameToEnumTable),
+                                 devName);
+            if (type != AUDIO_DEVICE_NONE) {
+                add(new DeviceDescriptor(String8(""), type));
+            } else {
+                sp<DeviceDescriptor> deviceDesc =
+                        declaredDevices.getDeviceFromName(String8(devName));
+                if (deviceDesc != 0) {
+                    add(deviceDesc);
+                }
+            }
+         }
+        devName = strtok(NULL, "|");
+     }
+}
+
 sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice(
                                                         audio_devices_t type, String8 address) const
 {
@@ -4423,6 +5436,20 @@
     return device;
 }
 
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId(
+                                                                    audio_port_handle_t id) const
+{
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < size(); i++) {
+        ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%d)->mId %d", id, i, itemAt(i)->mId);
+        if (itemAt(i)->mId == id) {
+            device = itemAt(i);
+            break;
+        }
+    }
+    return device;
+}
+
 AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType(
                                                                         audio_devices_t type) const
 {
@@ -4438,51 +5465,83 @@
     return devices;
 }
 
-void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(struct audio_port_config *config) const
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName(
+        const String8& name) const
 {
-    config->id = mId;
-    config->role = audio_is_output_device(mDeviceType) ?
+    sp<DeviceDescriptor> device;
+    for (size_t i = 0; i < size(); i++) {
+        if (itemAt(i)->mName == name) {
+            device = itemAt(i);
+            break;
+        }
+    }
+    return device;
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(
+                                                    struct audio_port_config *dstConfig,
+                                                    const struct audio_port_config *srcConfig) const
+{
+    dstConfig->id = mId;
+    dstConfig->role = audio_is_output_device(mDeviceType) ?
                         AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
-    config->type = AUDIO_PORT_TYPE_DEVICE;
-    config->sample_rate = 0;
-    config->channel_mask = mChannelMask;
-    config->format = AUDIO_FORMAT_DEFAULT;
-    config->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
-    config->gain.index = -1;
-    config->ext.device.type = mDeviceType;
-    strncpy(config->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+    dstConfig->type = AUDIO_PORT_TYPE_DEVICE;
+    dstConfig->channel_mask = mChannelMask;
+    dstConfig->gain.index = -1;
+    dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    // use supplied variable configuration parameters if any
+    if (srcConfig != NULL) {
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+            dstConfig->channel_mask = srcConfig->channel_mask;
+        }
+        if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+            dstConfig->gain = srcConfig->gain;
+            dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        }
+    }
+    dstConfig->ext.device.type = mDeviceType;
+    dstConfig->ext.device.hw_module = mModule->mHandle;
+    strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
 }
 
 void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const
 {
+    ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType);
     AudioPort::toAudioPort(port);
     port->id = mId;
+    toAudioPortConfig(&port->active_config);
     port->ext.device.type = mDeviceType;
+    port->ext.device.hw_module = mModule->mHandle;
     strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
 }
 
-void AudioPolicyManager::DeviceDescriptor::dumpHeader(int fd, int spaces)
+status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const
 {
     const size_t SIZE = 256;
     char buffer[SIZE];
+    String8 result;
 
-    snprintf(buffer, SIZE, "%*s%-48s %-2s %-8s %-32s \n",
-                         spaces, "", "Type", "ID", "Cnl Mask", "Address");
-    write(fd, buffer, strlen(buffer));
-}
-
-status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces) const
-{
-    const size_t SIZE = 256;
-    char buffer[SIZE];
-
-    snprintf(buffer, SIZE, "%*s%-48s %2d %08x %-32s \n",
-                         spaces, "",
-                         enumToString(sDeviceNameToEnumTable,
-                                      ARRAY_SIZE(sDeviceNameToEnumTable),
-                                      mDeviceType),
-                         mId, mChannelMask, mAddress.string());
-    write(fd, buffer, strlen(buffer));
+    snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1);
+    result.append(buffer);
+    if (mId != 0) {
+        snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId);
+        result.append(buffer);
+    }
+    snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
+                                              enumToString(sDeviceNameToEnumTable,
+                                                           ARRAY_SIZE(sDeviceNameToEnumTable),
+                                                           mDeviceType));
+    result.append(buffer);
+    if (mAddress.size() != 0) {
+        snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string());
+        result.append(buffer);
+    }
+    if (mChannelMask != AUDIO_CHANNEL_NONE) {
+        snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", spaces, "", mChannelMask);
+        result.append(buffer);
+    }
+    write(fd, result.string(), result.size());
+    AudioPort::dump(fd, spaces);
 
     return NO_ERROR;
 }
@@ -4531,102 +5590,30 @@
     return device;
 }
 
-status_t AudioPolicyManager::loadInput(cnode *root, HwModule *module)
-{
-    cnode *node = root->first_child;
-
-    sp<IOProfile> profile = new IOProfile(AUDIO_PORT_ROLE_SINK, module);
-
-    while (node) {
-        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
-            profile->loadSamplingRates((char *)node->value);
-        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
-            profile->loadFormats((char *)node->value);
-        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
-            profile->loadInChannels((char *)node->value);
-        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
-        }
-        node = node->next;
-    }
-    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
-            "loadInput() invalid supported devices");
-    ALOGW_IF(profile->mChannelMasks.size() == 0,
-            "loadInput() invalid supported channel masks");
-    ALOGW_IF(profile->mSamplingRates.size() == 0,
-            "loadInput() invalid supported sampling rates");
-    ALOGW_IF(profile->mFormats.size() == 0,
-            "loadInput() invalid supported formats");
-    if (!profile->mSupportedDevices.isEmpty() &&
-            (profile->mChannelMasks.size() != 0) &&
-            (profile->mSamplingRates.size() != 0) &&
-            (profile->mFormats.size() != 0)) {
-
-        ALOGV("loadInput() adding input Supported Devices %04x",
-              profile->mSupportedDevices.types());
-
-        module->mInputProfiles.add(profile);
-        return NO_ERROR;
-    } else {
-        return BAD_VALUE;
-    }
-}
-
-status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
-{
-    cnode *node = root->first_child;
-
-    sp<IOProfile> profile = new IOProfile(AUDIO_PORT_ROLE_SOURCE, module);
-
-    while (node) {
-        if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
-            profile->loadSamplingRates((char *)node->value);
-        } else if (strcmp(node->name, FORMATS_TAG) == 0) {
-            profile->loadFormats((char *)node->value);
-        } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
-            profile->loadOutChannels((char *)node->value);
-        } else if (strcmp(node->name, DEVICES_TAG) == 0) {
-            profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
-        } else if (strcmp(node->name, FLAGS_TAG) == 0) {
-            profile->mFlags = parseFlagNames((char *)node->value);
-        }
-        node = node->next;
-    }
-    ALOGW_IF(profile->mSupportedDevices.isEmpty(),
-            "loadOutput() invalid supported devices");
-    ALOGW_IF(profile->mChannelMasks.size() == 0,
-            "loadOutput() invalid supported channel masks");
-    ALOGW_IF(profile->mSamplingRates.size() == 0,
-            "loadOutput() invalid supported sampling rates");
-    ALOGW_IF(profile->mFormats.size() == 0,
-            "loadOutput() invalid supported formats");
-    if (!profile->mSupportedDevices.isEmpty() &&
-            (profile->mChannelMasks.size() != 0) &&
-            (profile->mSamplingRates.size() != 0) &&
-            (profile->mFormats.size() != 0)) {
-
-        ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
-              profile->mSupportedDevices.types(), profile->mFlags);
-
-        module->mOutputProfiles.add(profile);
-        return NO_ERROR;
-    } else {
-        return BAD_VALUE;
-    }
-}
-
 void AudioPolicyManager::loadHwModule(cnode *root)
 {
-    cnode *node = config_find(root, OUTPUTS_TAG);
     status_t status = NAME_NOT_FOUND;
-
+    cnode *node;
     HwModule *module = new HwModule(root->name);
 
+    node = config_find(root, DEVICES_TAG);
+    if (node != NULL) {
+        node = node->first_child;
+        while (node) {
+            ALOGV("loadHwModule() loading device %s", node->name);
+            status_t tmpStatus = module->loadDevice(node);
+            if (status == NAME_NOT_FOUND || status == NO_ERROR) {
+                status = tmpStatus;
+            }
+            node = node->next;
+        }
+    }
+    node = config_find(root, OUTPUTS_TAG);
     if (node != NULL) {
         node = node->first_child;
         while (node) {
             ALOGV("loadHwModule() loading output %s", node->name);
-            status_t tmpStatus = loadOutput(node, module);
+            status_t tmpStatus = module->loadOutput(node);
             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
                 status = tmpStatus;
             }
@@ -4638,13 +5625,15 @@
         node = node->first_child;
         while (node) {
             ALOGV("loadHwModule() loading input %s", node->name);
-            status_t tmpStatus = loadInput(node, module);
+            status_t tmpStatus = module->loadInput(node);
             if (status == NAME_NOT_FOUND || status == NO_ERROR) {
                 status = tmpStatus;
             }
             node = node->next;
         }
     }
+    loadGlobalConfig(root, module);
+
     if (status == NO_ERROR) {
         mHwModules.add(module);
     } else {
@@ -4667,16 +5656,22 @@
     }
 }
 
-void AudioPolicyManager::loadGlobalConfig(cnode *root)
+void AudioPolicyManager::loadGlobalConfig(cnode *root, HwModule *module)
 {
     cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
     if (node == NULL) {
         return;
     }
+    DeviceVector declaredDevices;
+    if (module != NULL) {
+        declaredDevices = module->mDeclaredDevices;
+    }
+
     node = node->first_child;
     while (node) {
         if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
-            mAvailableOutputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+            mAvailableOutputDevices.loadDevicesFromName((char *)node->value,
+                                                        declaredDevices);
             ALOGV("loadGlobalConfig() Attached Output Devices %08x",
                   mAvailableOutputDevices.types());
         } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
@@ -4684,13 +5679,14 @@
                                               ARRAY_SIZE(sDeviceNameToEnumTable),
                                               (char *)node->value);
             if (device != AUDIO_DEVICE_NONE) {
-                mDefaultOutputDevice = new DeviceDescriptor(device);
+                mDefaultOutputDevice = new DeviceDescriptor(String8(""), device);
             } else {
                 ALOGW("loadGlobalConfig() default device not specified");
             }
             ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mDeviceType);
         } else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
-            mAvailableInputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+            mAvailableInputDevices.loadDevicesFromName((char *)node->value,
+                                                       declaredDevices);
             ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types());
         } else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
             mSpeakerDrcEnabled = stringToBool((char *)node->value);
@@ -4712,9 +5708,9 @@
     root = config_node("", "");
     config_load(root, data);
 
-    loadGlobalConfig(root);
     loadHwModules(root);
-
+    // legacy audio_policy.conf files have one global_configuration section
+    loadGlobalConfig(root, getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
     config_free(root);
     free(root);
     free(data);
@@ -4728,13 +5724,13 @@
 {
     HwModule *module;
     sp<IOProfile> profile;
-    sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
+    sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_IN_BUILTIN_MIC);
     mAvailableOutputDevices.add(mDefaultOutputDevice);
     mAvailableInputDevices.add(defaultInputDevice);
 
     module = new HwModule("primary");
 
-    profile = new IOProfile(AUDIO_PORT_ROLE_SOURCE, module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
     profile->mSamplingRates.add(44100);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4742,7 +5738,7 @@
     profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
     module->mOutputProfiles.add(profile);
 
-    profile = new IOProfile(AUDIO_PORT_ROLE_SINK, module);
+    profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
     profile->mSamplingRates.add(8000);
     profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
     profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index 905a3c8..e012d63 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -140,6 +140,23 @@
 
         virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
 
+        virtual status_t listAudioPorts(audio_port_role_t role,
+                                        audio_port_type_t type,
+                                        unsigned int *num_ports,
+                                        struct audio_port *ports,
+                                        unsigned int *generation);
+        virtual status_t getAudioPort(struct audio_port *port);
+        virtual status_t createAudioPatch(const struct audio_patch *patch,
+                                           audio_patch_handle_t *handle,
+                                           uid_t uid);
+        virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+                                              uid_t uid);
+        virtual status_t listAudioPatches(unsigned int *num_patches,
+                                          struct audio_patch *patches,
+                                          unsigned int *generation);
+        virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+        virtual void clearAudioPatches(uid_t uid);
+
 protected:
 
         enum routing_strategy {
@@ -173,26 +190,25 @@
             DEVICE_CATEGORY_CNT
         };
 
-        class IOProfile;
+        class HwModule;
 
-        class HwModule {
+        class AudioGain: public RefBase
+        {
         public:
-                    HwModule(const char *name);
-                    ~HwModule();
+            AudioGain();
+            virtual ~AudioGain() {}
 
-            void dump(int fd);
+            void dump(int fd, int spaces, int index) const;
 
-            const char *const mName; // base name of the audio HW module (primary, a2dp ...)
-            audio_module_handle_t mHandle;
-            Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
-            Vector < sp<IOProfile> > mInputProfiles;  // input profiles exposed by this module
+            struct audio_gain mGain;
         };
 
         class AudioPort: public RefBase
         {
         public:
-            AudioPort(audio_port_type_t type, audio_port_role_t role, HwModule *module) :
-                mType(type), mRole(role), mModule(module) {}
+            AudioPort(const String8& name, audio_port_type_t type,
+                      audio_port_role_t role, HwModule *module) :
+                mName(name), mType(type), mRole(role), mModule(module) {}
             virtual ~AudioPort() {}
 
             virtual void toAudioPort(struct audio_port *port) const;
@@ -202,32 +218,52 @@
             void loadOutChannels(char *name);
             void loadInChannels(char *name);
 
-            audio_port_type_t        mType;
-            audio_port_role_t        mRole;
+            audio_gain_mode_t loadGainMode(char *name);
+            void loadGain(cnode *root);
+            void loadGains(cnode *root);
+
+            void dump(int fd, int spaces) const;
+
+            String8           mName;
+            audio_port_type_t mType;
+            audio_port_role_t mRole;
             // by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
             // indicates the supported parameters should be read from the output stream
             // after it is opened for the first time
             Vector <uint32_t> mSamplingRates; // supported sampling rates
             Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
             Vector <audio_format_t> mFormats; // supported audio formats
-            HwModule *mModule;                     // audio HW module exposing this I/O stream
+            Vector < sp<AudioGain> > mGains; // gain controllers
+            HwModule *mModule;                 // audio HW module exposing this I/O stream
         };
 
+        class AudioPatch: public RefBase
+        {
+        public:
+            AudioPatch(audio_patch_handle_t handle,
+                       const struct audio_patch *patch, uid_t uid) :
+                           mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {}
+
+            audio_patch_handle_t mHandle;
+            struct audio_patch mPatch;
+            uid_t mUid;
+            audio_patch_handle_t mAfPatchHandle;
+        };
 
         class DeviceDescriptor: public AudioPort
         {
         public:
-            DeviceDescriptor(audio_devices_t type, String8 address,
+            DeviceDescriptor(const String8& name, audio_devices_t type, String8 address,
                              audio_channel_mask_t channelMask) :
-                                 AudioPort(AUDIO_PORT_TYPE_DEVICE,
+                                 AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
                                            audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
                                                                           AUDIO_PORT_ROLE_SOURCE,
                                          NULL),
                                  mDeviceType(type), mAddress(address),
                                  mChannelMask(channelMask), mId(0) {}
 
-            DeviceDescriptor(audio_devices_t type) :
-                                AudioPort(AUDIO_PORT_TYPE_DEVICE,
+            DeviceDescriptor(String8 name, audio_devices_t type) :
+                                AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
                                           audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
                                                                          AUDIO_PORT_ROLE_SOURCE,
                                         NULL),
@@ -236,11 +272,12 @@
             virtual ~DeviceDescriptor() {}
 
             bool equals(const sp<DeviceDescriptor>& other) const;
-            void toAudioPortConfig(struct audio_port_config *config) const;
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
+
             virtual void toAudioPort(struct audio_port *port) const;
 
-            status_t dump(int fd, int spaces) const;
-            static void dumpHeader(int fd, int spaces);
+            status_t dump(int fd, int spaces, int index) const;
 
             audio_devices_t mDeviceType;
             String8 mAddress;
@@ -260,8 +297,12 @@
             audio_devices_t types() const { return mDeviceTypes; }
 
             void loadDevicesFromType(audio_devices_t types);
+            void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+
             sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
             DeviceVector getDevicesFromType(audio_devices_t types) const;
+            sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
+            sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
 
         private:
             void refreshTypes();
@@ -276,7 +317,7 @@
         class IOProfile : public AudioPort
         {
         public:
-            IOProfile(audio_port_role_t role, HwModule *module);
+            IOProfile(const String8& name, audio_port_role_t role, HwModule *module);
             virtual ~IOProfile();
 
             bool isCompatibleProfile(audio_devices_t device,
@@ -294,6 +335,25 @@
                                                 // direct output...). For outputs only.
         };
 
+        class HwModule {
+        public:
+                    HwModule(const char *name);
+                    ~HwModule();
+
+            status_t loadOutput(cnode *root);
+            status_t loadInput(cnode *root);
+            status_t loadDevice(cnode *root);
+
+            void dump(int fd);
+
+            const char *const mName; // base name of the audio HW module (primary, a2dp ...)
+            audio_module_handle_t mHandle;
+            Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
+            Vector < sp<IOProfile> > mInputProfiles;  // input profiles exposed by this module
+            DeviceVector             mDeclaredDevices; // devices declared in audio_policy.conf
+
+        };
+
         // default volume curve
         static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManager::VOLCNT];
         // default volume curve for media strategy
@@ -335,7 +395,8 @@
                              uint32_t inPastMs = 0,
                              nsecs_t sysTime = 0) const;
 
-            void toAudioPortConfig(struct audio_port_config *config) const;
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
             void toAudioPort(struct audio_port *port) const;
 
             audio_port_handle_t mId;
@@ -379,7 +440,8 @@
             audio_source_t mInputSource;                // input source selected by application (mediarecorder.h)
             const sp<IOProfile> mProfile;                  // I/O profile this output derives from
 
-            void toAudioPortConfig(struct audio_port_config *config) const;
+            void toAudioPortConfig(struct audio_port_config *dstConfig,
+                                   const struct audio_port_config *srcConfig = NULL) const;
             void toAudioPort(struct audio_port *port) const;
         };
 
@@ -439,13 +501,17 @@
         uint32_t setOutputDevice(audio_io_handle_t output,
                              audio_devices_t device,
                              bool force = false,
-                             int delayMs = 0);
+                             int delayMs = 0,
+                             audio_patch_handle_t *patchHandle = NULL);
         status_t resetOutputDevice(audio_io_handle_t output,
-                                   int delayMs = 0);
+                                   int delayMs = 0,
+                                   audio_patch_handle_t *patchHandle = NULL);
         status_t setInputDevice(audio_io_handle_t input,
                                 audio_devices_t device,
-                                bool force = false);
-        status_t resetInputDevice(audio_io_handle_t input);
+                                bool force = false,
+                                audio_patch_handle_t *patchHandle = NULL);
+        status_t resetInputDevice(audio_io_handle_t input,
+                                  audio_patch_handle_t *patchHandle = NULL);
 
         // select input device corresponding to requested audio source
         virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource);
@@ -589,6 +655,14 @@
 
         bool isNonOffloadableEffectEnabled();
 
+        status_t addAudioPatch(audio_patch_handle_t handle,
+                               const sp<AudioPatch>& patch);
+        status_t removeAudioPatch(audio_patch_handle_t handle);
+
+        AudioOutputDescriptor *getOutputFromId(audio_port_handle_t id) const;
+        AudioInputDescriptor *getInputFromId(audio_port_handle_t id) const;
+        HwModule *getModuleForDevice(audio_devices_t device) const;
+        HwModule *getModuleFromName(const char *name) const;
         //
         // Audio policy configuration file parsing (audio_policy.conf)
         //
@@ -601,15 +675,14 @@
         static bool stringToBool(const char *value);
         static audio_output_flags_t parseFlagNames(char *name);
         static audio_devices_t parseDeviceNames(char *name);
-        status_t loadOutput(cnode *root,  HwModule *module);
-        status_t loadInput(cnode *root,  HwModule *module);
         void loadHwModule(cnode *root);
         void loadHwModules(cnode *root);
-        void loadGlobalConfig(cnode *root);
+        void loadGlobalConfig(cnode *root, HwModule *module);
         status_t loadAudioPolicyConfig(const char *path);
         void defaultAudioPolicyConfig(void);
 
 
+        uid_t mUidCached;
         AudioPolicyClientInterface *mpClientInterface;  // audio policy client interface
         audio_io_handle_t mPrimaryOutput;              // primary output handle
         // list of descriptors for outputs currently opened
@@ -618,10 +691,8 @@
         // reset to mOutputs when updateDevicesAndOutputs() is called.
         DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs;
         DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs;     // list of input descriptors
-        DeviceVector  mAvailableOutputDevices; // bit field of all available output devices
-        DeviceVector  mAvailableInputDevices; // bit field of all available input devices
-                                                // without AUDIO_DEVICE_BIT_IN to allow direct bit
-                                                // field comparisons
+        DeviceVector  mAvailableOutputDevices; // all available output devices
+        DeviceVector  mAvailableInputDevices;  // all available input devices
         int mPhoneState;                                                    // current phone state
         audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT];   // current forced use configuration
 
@@ -644,6 +715,9 @@
 
         Vector <HwModule *> mHwModules;
         volatile int32_t mNextUniqueId;
+        volatile int32_t mAudioPortGeneration;
+
+        DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches;
 
 #ifdef AUDIO_POLICY_TEST
         Mutex   mLock;
@@ -668,6 +742,8 @@
         void handleNotificationRoutingForStream(audio_stream_type_t stream);
         static bool isVirtualInputDevice(audio_devices_t device);
         uint32_t nextUniqueId();
+        uint32_t nextAudioPortGeneration();
+        uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; }
         // converts device address to string sent to audio HAL via setParameters
         static String8 addressToParameter(audio_devices_t device, const String8 address);
 };
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index ea573a4..a2a0461 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -148,6 +148,61 @@
     delete mAudioPolicyManager;
     delete mAudioPolicyClient;
 #endif
+
+    mNotificationClients.clear();
+}
+
+// A notification client is always registered by AudioSystem when the client process
+// connects to AudioPolicyService.
+void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+{
+
+    Mutex::Autolock _l(mLock);
+
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+    if (mNotificationClients.indexOfKey(uid) < 0) {
+        sp<NotificationClient> notificationClient = new NotificationClient(this,
+                                                                           client,
+                                                                           uid);
+        ALOGV("registerClient() client %p, uid %d", client.get(), uid);
+
+        mNotificationClients.add(uid, notificationClient);
+
+        sp<IBinder> binder = client->asBinder();
+        binder->linkToDeath(notificationClient);
+    }
+}
+
+// removeNotificationClient() is called when the client process dies.
+void AudioPolicyService::removeNotificationClient(uid_t uid)
+{
+    Mutex::Autolock _l(mLock);
+
+    mNotificationClients.removeItem(uid);
+
+#ifndef USE_LEGACY_AUDIO_POLICY
+        if (mAudioPolicyManager) {
+            mAudioPolicyManager->clearAudioPatches(uid);
+        }
+#endif
+}
+
+void AudioPolicyService::onAudioPortListUpdate()
+{
+    mOutputCommandThread->updateAudioPortListCommand();
+}
+
+void AudioPolicyService::doOnAudioPortListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::onAudioPatchListUpdate()
+{
+    mOutputCommandThread->updateAudioPatchListCommand();
 }
 
 status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -163,6 +218,53 @@
     return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs);
 }
 
+void AudioPolicyService::doOnAudioPatchListUpdate()
+{
+    Mutex::Autolock _l(mLock);
+    for (size_t i = 0; i < mNotificationClients.size(); i++) {
+        mNotificationClients.valueAt(i)->onAudioPatchListUpdate();
+    }
+}
+
+status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs)
+{
+    return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
+}
+
+AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
+                                                     const sp<IAudioPolicyServiceClient>& client,
+                                                     uid_t uid)
+    : mService(service), mUid(uid), mAudioPolicyServiceClient(client)
+{
+}
+
+AudioPolicyService::NotificationClient::~NotificationClient()
+{
+}
+
+void AudioPolicyService::NotificationClient::binderDied(const wp<IBinder>& who __unused)
+{
+    sp<NotificationClient> keep(this);
+    sp<AudioPolicyService> service = mService.promote();
+    if (service != 0) {
+        service->removeNotificationClient(mUid);
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPortListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPortListUpdate();
+    }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPatchListUpdate()
+{
+    if (mAudioPolicyServiceClient != 0) {
+        mAudioPolicyServiceClient->onAudioPatchListUpdate();
+    }
+}
 
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
     ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
@@ -390,6 +492,36 @@
                         command->mStatus = af->releaseAudioPatch(data->mHandle);
                     }
                     } break;
+                case UPDATE_AUDIOPORT_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio port list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPortListUpdate();
+                    mLock.lock();
+                    }break;
+                case UPDATE_AUDIOPATCH_LIST: {
+                    ALOGV("AudioCommandThread() processing update audio patch list");
+                    sp<AudioPolicyService> svc = mService.promote();
+                    if (svc == 0) {
+                        break;
+                    }
+                    mLock.unlock();
+                    svc->doOnAudioPatchListUpdate();
+                    mLock.lock();
+                    }break;
+                case SET_AUDIOPORT_CONFIG: {
+                    SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get();
+                    ALOGV("AudioCommandThread() processing set port config");
+                    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+                    if (af == 0) {
+                        command->mStatus = PERMISSION_DENIED;
+                    } else {
+                        command->mStatus = af->setAudioPortConfig(&data->mConfig);
+                    }
+                    } break;
                 default:
                     ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                 }
@@ -584,6 +716,35 @@
     return sendCommand(command, delayMs);
 }
 
+void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPORT_LIST;
+    ALOGV("AudioCommandThread() adding update audio port list");
+    sendCommand(command);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
+{
+    sp<AudioCommand>command = new AudioCommand();
+    command->mCommand = UPDATE_AUDIOPATCH_LIST;
+    ALOGV("AudioCommandThread() adding update audio patch list");
+    sendCommand(command);
+}
+
+status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand(
+                                            const struct audio_port_config *config, int delayMs)
+{
+    sp<AudioCommand> command = new AudioCommand();
+    command->mCommand = SET_AUDIOPORT_CONFIG;
+    SetAudioPortConfigData *data = new SetAudioPortConfigData();
+    data->mConfig = *config;
+    command->mParam = data;
+    command->mWaitStatus = true;
+    ALOGV("AudioCommandThread() adding set port config delay %d", delayMs);
+    return sendCommand(command, delayMs);
+}
+
 status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
 {
     {
@@ -602,7 +763,6 @@
     return command->mStatus;
 }
 
-
 // insertCommand_l() must be called with mLock held
 void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& command, int delayMs)
 {
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 9f88b1e..40f589b 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -154,6 +154,8 @@
                                       unsigned int *generation);
     virtual status_t setAudioPortConfig(const struct audio_port_config *config);
 
+    virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+
             status_t doStopOutput(audio_io_handle_t output,
                                   audio_stream_type_t stream,
                                   int session = 0);
@@ -164,6 +166,14 @@
                                       int delayMs);
             status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
                                              int delayMs);
+            virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+                                                      int delayMs);
+
+            void removeNotificationClient(uid_t uid);
+            void onAudioPortListUpdate();
+            void doOnAudioPortListUpdate();
+            void onAudioPatchListUpdate();
+            void doOnAudioPatchListUpdate();
 
 private:
                         AudioPolicyService() ANDROID_API;
@@ -192,6 +202,9 @@
             RELEASE_OUTPUT,
             CREATE_AUDIO_PATCH,
             RELEASE_AUDIO_PATCH,
+            UPDATE_AUDIOPORT_LIST,
+            UPDATE_AUDIOPATCH_LIST,
+            SET_AUDIOPORT_CONFIG,
         };
 
         AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -223,7 +236,10 @@
                                                         int delayMs);
                     status_t    releaseAudioPatchCommand(audio_patch_handle_t handle,
                                                          int delayMs);
-
+                    void        updateAudioPortListCommand();
+                    void        updateAudioPatchListCommand();
+                    status_t    setAudioPortConfigCommand(const struct audio_port_config *config,
+                                                          int delayMs);
                     void        insertCommand_l(AudioCommand *command, int delayMs = 0);
 
     private:
@@ -301,6 +317,11 @@
             audio_patch_handle_t mHandle;
         };
 
+        class SetAudioPortConfigData : public AudioCommandData {
+        public:
+            struct audio_port_config mConfig;
+        };
+
         Mutex   mLock;
         Condition mWaitWorkCV;
         Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -454,10 +475,39 @@
         virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
                                            int delayMs);
 
+        /* Set audio port configuration */
+        virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs);
+
+        virtual void onAudioPortListUpdate();
+        virtual void onAudioPatchListUpdate();
+
      private:
         AudioPolicyService *mAudioPolicyService;
     };
 
+    // --- Notification Client ---
+    class NotificationClient : public IBinder::DeathRecipient {
+    public:
+                            NotificationClient(const sp<AudioPolicyService>& service,
+                                                const sp<IAudioPolicyServiceClient>& client,
+                                                uid_t uid);
+        virtual             ~NotificationClient();
+
+                            void        onAudioPortListUpdate();
+                            void        onAudioPatchListUpdate();
+
+                // IBinder::DeathRecipient
+                virtual     void        binderDied(const wp<IBinder>& who);
+
+    private:
+                            NotificationClient(const NotificationClient&);
+                            NotificationClient& operator = (const NotificationClient&);
+
+        const wp<AudioPolicyService>        mService;
+        const uid_t                         mUid;
+        const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+    };
+
     static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
 
     void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
@@ -494,6 +544,8 @@
 
     KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
     KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
+
+    DefaultKeyedVector< uid_t, sp<NotificationClient> >    mNotificationClients;
 };
 
 }; // namespace android
diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h
new file mode 100644
index 0000000..79f20f1
--- /dev/null
+++ b/services/audiopolicy/audio_policy_conf.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#ifndef ANDROID_AUDIO_POLICY_CONF_H
+#define ANDROID_AUDIO_POLICY_CONF_H
+
+
+/////////////////////////////////////////////////
+//      Definitions for audio policy configuration file (audio_policy.conf)
+/////////////////////////////////////////////////
+
+#define AUDIO_HARDWARE_MODULE_ID_MAX_LEN 32
+
+#define AUDIO_POLICY_CONFIG_FILE "/system/etc/audio_policy.conf"
+#define AUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf"
+
+// global configuration
+#define GLOBAL_CONFIG_TAG "global_configuration"
+
+#define ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices"
+#define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device"
+#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices"
+#define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled"
+
+// hw modules descriptions
+#define AUDIO_HW_MODULE_TAG "audio_hw_modules"
+
+#define OUTPUTS_TAG "outputs"
+#define INPUTS_TAG "inputs"
+
+#define SAMPLING_RATES_TAG "sampling_rates"
+#define FORMATS_TAG "formats"
+#define CHANNELS_TAG "channel_masks"
+#define DEVICES_TAG "devices"
+#define FLAGS_TAG "flags"
+
+#define DYNAMIC_VALUE_TAG "dynamic" // special value for "channel_masks", "sampling_rates" and
+                                    // "formats" in outputs descriptors indicating that supported
+                                    // values should be queried after opening the output.
+
+#define DEVICES_TAG "devices"
+#define DEVICE_TYPE "type"
+#define DEVICE_ADDRESS "address"
+
+#define MIXERS_TAG "mixers"
+#define MIXER_TYPE "type"
+#define MIXER_TYPE_MUX "mux"
+#define MIXER_TYPE_MIX "mix"
+
+#define GAINS_TAG "gains"
+#define GAIN_MODE "mode"
+#define GAIN_CHANNELS "channel_mask"
+#define GAIN_MIN_VALUE "min_value_mB"
+#define GAIN_MAX_VALUE "max_value_mB"
+#define GAIN_DEFAULT_VALUE "default_value_mB"
+#define GAIN_STEP_VALUE "step_value_mB"
+#define GAIN_MIN_RAMP_MS "min_ramp_ms"
+#define GAIN_MAX_RAMP_MS "max_ramp_ms"
+
+
+
+#endif  // ANDROID_AUDIO_POLICY_CONF_H