Merge "Change parameter type for volume to float in AudioMixer"
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/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 219dbfd..0dbfa62 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -134,10 +134,17 @@
         ssize_t filled = rear - front;
         // pipe should not be overfull
         if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
-            ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
-            mIsShutdown = true;
-            status = NO_INIT;
-            goto end;
+            if (mIsOut) {
+                ALOGE("Shared memory control block is corrupt (filled=%d, mFrameCount=%u); "
+                        "shutting down", filled, mFrameCount);
+                mIsShutdown = true;
+                status = NO_INIT;
+                goto end;
+            }
+            // for input, sync up on overrun
+            filled = 0;
+            cblk->u.mStreaming.mFront = rear;
+            (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
         }
         // don't allow filling pipe beyond the nominal size
         size_t avail = mIsOut ? mFrameCount - filled : filled;
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/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index e07b6aa..05caf75 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -2460,8 +2460,9 @@
         }
     }
 
-    if (!track->sampleTable->isValid()) {
+    if (track->sampleTable == NULL || !track->sampleTable->isValid()) {
         // Make sure we have all the metadata we need.
+        ALOGE("stbl atom missing/invalid.");
         return ERROR_MALFORMED;
     }
 
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 2ac16c7..c5d8858 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaCodec"
 
 #include "NdkMediaCodec.h"
diff --git a/media/ndk/NdkMediaCrypto.cpp b/media/ndk/NdkMediaCrypto.cpp
index cbadea5..1cc2f1a 100644
--- a/media/ndk/NdkMediaCrypto.cpp
+++ b/media/ndk/NdkMediaCrypto.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaCrypto"
 
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index a0cbb70..7a1048c 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaDrm"
 
 #include "NdkMediaDrm.h"
diff --git a/media/ndk/NdkMediaExtractor.cpp b/media/ndk/NdkMediaExtractor.cpp
index f9f9ac3..492e002 100644
--- a/media/ndk/NdkMediaExtractor.cpp
+++ b/media/ndk/NdkMediaExtractor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaExtractor"
 
 
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 77018ec..67dc2c2 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaFormat"
 
 
diff --git a/media/ndk/NdkMediaMuxer.cpp b/media/ndk/NdkMediaMuxer.cpp
index 50fc336..b1b0362 100644
--- a/media/ndk/NdkMediaMuxer.cpp
+++ b/media/ndk/NdkMediaMuxer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #define LOG_TAG "NdkMediaMuxer"
 
 
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/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 5b09d54..527fd65 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -169,7 +169,8 @@
       mBtNrecIsOff(false),
       mIsLowRamDevice(true),
       mIsDeviceTypeKnown(false),
-      mGlobalEffectEnableTime(0)
+      mGlobalEffectEnableTime(0),
+      mPrimaryOutputSampleRate(0)
 {
     getpid_cached = getpid();
     char value[PROPERTY_VALUE_MAX];
@@ -1679,6 +1680,8 @@
             mHardwareStatus = AUDIO_HW_SET_MODE;
             hwDevHal->set_mode(hwDevHal, mMode);
             mHardwareStatus = AUDIO_HW_IDLE;
+
+            mPrimaryOutputSampleRate = config.sample_rate;
         }
         return id;
     }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 29dc6b2..6e73a14 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -50,6 +50,8 @@
 
 #include <media/AudioBufferProvider.h>
 #include <media/ExtendedAudioBufferProvider.h>
+
+#include "FastCapture.h"
 #include "FastMixer.h"
 #include <media/nbaio/NBAIO.h>
 #include "AudioWatchdog.h"
@@ -691,6 +693,9 @@
     nsecs_t mGlobalEffectEnableTime;  // when a global effect was last enabled
 
     sp<PatchPanel> mPatchPanel;
+
+    uint32_t    mPrimaryOutputSampleRate;   // sample rate of the primary output, or zero if none
+                                            // protected by mHardwareLock
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
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/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index eee74b3..02b5094 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -179,22 +179,29 @@
                 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
                 if (patch->sinks[i].ext.mix.hw_module != src_module) {
-                    ALOGW("createAudioPatch() cannot connect source on module %d to"
+                    ALOGW("createAudioPatch() cannot connect source on module %d to "
                             "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 89f735b..d08c966 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -38,6 +38,7 @@
 #include <audio_utils/minifloat.h>
 
 // NBAIO implementations
+#include <media/nbaio/AudioStreamInSource.h>
 #include <media/nbaio/AudioStreamOutSink.h>
 #include <media/nbaio/MonoPipe.h>
 #include <media/nbaio/MonoPipeReader.h>
@@ -53,6 +54,7 @@
 #include "AudioFlinger.h"
 #include "AudioMixer.h"
 #include "FastMixer.h"
+#include "FastCapture.h"
 #include "ServiceUtilities.h"
 #include "SchedulingPolicyService.h"
 
@@ -131,9 +133,17 @@
     //  up large writes into smaller ones, and the wrapper would need to deal with scheduler.
 } kUseFastMixer = FastMixer_Static;
 
+// Whether to use fast capture
+static const enum {
+    FastCapture_Never,  // never initialize or use: for debugging only
+    FastCapture_Always, // always initialize and use, even if not needed: for debugging only
+    FastCapture_Static, // initialize if needed, then use all the time if initialized
+} kUseFastCapture = FastCapture_Static;
+
 // Priorities for requestPriority
 static const int kPriorityAudioApp = 2;
 static const int kPriorityFastMixer = 3;
+static const int kPriorityFastCapture = 3;
 
 // IAudioFlinger::createTrack() reports back to client the total size of shared memory area
 // for the track.  The client then sub-divides this into smaller buffers for its use.
@@ -4748,16 +4758,151 @@
 #endif
     , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize,
             "RecordThreadRO", MemoryHeapBase::READ_ONLY))
+    // mFastCapture below
+    , mFastCaptureFutex(0)
+    // mInputSource
+    // mPipeSink
+    // mPipeSource
+    , mPipeFramesP2(0)
+    // mPipeMemory
+    // mFastCaptureNBLogWriter
+    , mFastTrackAvail(true)
 {
     snprintf(mName, kNameLength, "AudioIn_%X", id);
     mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
 
     readInputParameters_l();
+
+    // create an NBAIO source for the HAL input stream, and negotiate
+    mInputSource = new AudioStreamInSource(input->stream);
+    size_t numCounterOffers = 0;
+    const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)};
+    ssize_t index = mInputSource->negotiate(offers, 1, NULL, numCounterOffers);
+    ALOG_ASSERT(index == 0);
+
+    // initialize fast capture depending on configuration
+    bool initFastCapture;
+    switch (kUseFastCapture) {
+    case FastCapture_Never:
+        initFastCapture = false;
+        break;
+    case FastCapture_Always:
+        initFastCapture = true;
+        break;
+    case FastCapture_Static:
+        uint32_t primaryOutputSampleRate;
+        {
+            AutoMutex _l(audioFlinger->mHardwareLock);
+            primaryOutputSampleRate = audioFlinger->mPrimaryOutputSampleRate;
+        }
+        initFastCapture =
+                // either capture sample rate is same as (a reasonable) primary output sample rate
+                (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) &&
+                    (mSampleRate == primaryOutputSampleRate)) ||
+                // or primary output sample rate is unknown, and capture sample rate is reasonable
+                ((primaryOutputSampleRate == 0) &&
+                    ((mSampleRate == 44100 || mSampleRate == 48000)))) &&
+                // and the buffer size is < 10 ms
+                (mFrameCount * 1000) / mSampleRate < 10;
+        break;
+    // case FastCapture_Dynamic:
+    }
+
+    if (initFastCapture) {
+        // create a Pipe for FastMixer to write to, and for us and fast tracks to read from
+        NBAIO_Format format = mInputSource->format();
+        size_t pipeFramesP2 = roundup(mFrameCount * 8);
+        size_t pipeSize = pipeFramesP2 * Format_frameSize(format);
+        void *pipeBuffer;
+        const sp<MemoryDealer> roHeap(readOnlyHeap());
+        sp<IMemory> pipeMemory;
+        if ((roHeap == 0) ||
+                (pipeMemory = roHeap->allocate(pipeSize)) == 0 ||
+                (pipeBuffer = pipeMemory->pointer()) == NULL) {
+            ALOGE("not enough memory for pipe buffer size=%zu", pipeSize);
+            goto failed;
+        }
+        // pipe will be shared directly with fast clients, so clear to avoid leaking old information
+        memset(pipeBuffer, 0, pipeSize);
+        Pipe *pipe = new Pipe(pipeFramesP2, format, pipeBuffer);
+        const NBAIO_Format offers[1] = {format};
+        size_t numCounterOffers = 0;
+        ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+        ALOG_ASSERT(index == 0);
+        mPipeSink = pipe;
+        PipeReader *pipeReader = new PipeReader(*pipe);
+        numCounterOffers = 0;
+        index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+        ALOG_ASSERT(index == 0);
+        mPipeSource = pipeReader;
+        mPipeFramesP2 = pipeFramesP2;
+        mPipeMemory = pipeMemory;
+
+        // create fast capture
+        mFastCapture = new FastCapture();
+        FastCaptureStateQueue *sq = mFastCapture->sq();
+#ifdef STATE_QUEUE_DUMP
+        // FIXME
+#endif
+        FastCaptureState *state = sq->begin();
+        state->mCblk = NULL;
+        state->mInputSource = mInputSource.get();
+        state->mInputSourceGen++;
+        state->mPipeSink = pipe;
+        state->mPipeSinkGen++;
+        state->mFrameCount = mFrameCount;
+        state->mCommand = FastCaptureState::COLD_IDLE;
+        // already done in constructor initialization list
+        //mFastCaptureFutex = 0;
+        state->mColdFutexAddr = &mFastCaptureFutex;
+        state->mColdGen++;
+        state->mDumpState = &mFastCaptureDumpState;
+#ifdef TEE_SINK
+        // FIXME
+#endif
+        mFastCaptureNBLogWriter = audioFlinger->newWriter_l(kFastCaptureLogSize, "FastCapture");
+        state->mNBLogWriter = mFastCaptureNBLogWriter.get();
+        sq->end();
+        sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+
+        // start the fast capture
+        mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO);
+        pid_t tid = mFastCapture->getTid();
+        int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
+        if (err != 0) {
+            ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+                    kPriorityFastCapture, getpid_cached, tid, err);
+        }
+
+#ifdef AUDIO_WATCHDOG
+        // FIXME
+#endif
+
+    }
+failed: ;
+
+    // FIXME mNormalSource
 }
 
 
 AudioFlinger::RecordThread::~RecordThread()
 {
+    if (mFastCapture != 0) {
+        FastCaptureStateQueue *sq = mFastCapture->sq();
+        FastCaptureState *state = sq->begin();
+        if (state->mCommand == FastCaptureState::COLD_IDLE) {
+            int32_t old = android_atomic_inc(&mFastCaptureFutex);
+            if (old == -1) {
+                (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1);
+            }
+        }
+        state->mCommand = FastCaptureState::EXIT;
+        sq->end();
+        sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+        mFastCapture->join();
+        mFastCapture.clear();
+    }
+    mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter);
     mAudioFlinger->unregisterWriter(mNBLogWriter);
     delete[] mRsmpInBuffer;
 }
@@ -4812,6 +4957,8 @@
         // activeTracks accumulates a copy of a subset of mActiveTracks
         Vector< sp<RecordTrack> > activeTracks;
 
+        // reference to the (first and only) fast track
+        sp<RecordTrack> fastTrack;
 
         { // scope for mLock
             Mutex::Autolock _l(mLock);
@@ -4893,6 +5040,11 @@
                 activeTracks.add(activeTrack);
                 i++;
 
+                if (activeTrack->isFastTrack()) {
+                    ALOG_ASSERT(!mFastTrackAvail);
+                    ALOG_ASSERT(fastTrack == 0);
+                    fastTrack = activeTrack;
+                }
             }
             if (doBroadcast) {
                 mStartStopCond.broadcast();
@@ -4918,6 +5070,36 @@
             effectChains[i]->process_l();
         }
 
+        // Start the fast capture if it's not already running
+        if (mFastCapture != 0) {
+            FastCaptureStateQueue *sq = mFastCapture->sq();
+            FastCaptureState *state = sq->begin();
+            if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME &&
+                    (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) {
+                if (state->mCommand == FastCaptureState::COLD_IDLE) {
+                    int32_t old = android_atomic_inc(&mFastCaptureFutex);
+                    if (old == -1) {
+                        (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1);
+                    }
+                }
+                state->mCommand = FastCaptureState::READ_WRITE;
+#if 0   // FIXME
+                mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
+                        FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
+#endif
+                state->mCblk = fastTrack != 0 ? fastTrack->cblk() : NULL;
+                sq->end();
+                sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED);
+#if 0
+                if (kUseFastCapture == FastCapture_Dynamic) {
+                    mNormalSource = mPipeSource;
+                }
+#endif
+            } else {
+                sq->end(false /*didModify*/);
+            }
+        }
+
         // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one.
         // Only the client(s) that are too slow will overrun. But if even the fastest client is too
         // slow, then this RecordThread will overrun by not calling HAL read often enough.
@@ -4925,24 +5107,45 @@
         // copy to the right place.  Permitted because mRsmpInBuffer was over-allocated.
 
         int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1);
-        ssize_t bytesRead = mInput->stream->read(mInput->stream,
-                &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
-        if (bytesRead <= 0) {
-            ALOGE("read failed: bytesRead=%d < %u", bytesRead, mBufferSize);
+        ssize_t framesRead;
+
+        // If an NBAIO source is present, use it to read the normal capture's data
+        if (mPipeSource != 0) {
+            size_t framesToRead = mBufferSize / mFrameSize;
+            framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount],
+                    framesToRead, AudioBufferProvider::kInvalidPTS);
+            if (framesRead == 0) {
+                // since pipe is non-blocking, simulate blocking input
+                sleepUs = (framesToRead * 1000000LL) / mSampleRate;
+            }
+        // otherwise use the HAL / AudioStreamIn directly
+        } else {
+            ssize_t bytesRead = mInput->stream->read(mInput->stream,
+                    &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+            if (bytesRead < 0) {
+                framesRead = bytesRead;
+            } else {
+                framesRead = bytesRead / mFrameSize;
+            }
+        }
+
+        if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) {
+            ALOGE("read failed: framesRead=%d", framesRead);
             // Force input into standby so that it tries to recover at next read attempt
             inputStandBy();
             sleepUs = kRecordThreadSleepUs;
+        }
+        if (framesRead <= 0) {
             continue;
         }
-        ALOG_ASSERT((size_t) bytesRead <= mBufferSize);
-        size_t framesRead = bytesRead / mFrameSize;
         ALOG_ASSERT(framesRead > 0);
+
         if (mTeeSink != 0) {
             (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead);
         }
         // If destination is non-contiguous, we now correct for reading past end of buffer.
         size_t part1 = mRsmpInFramesP2 - rear;
-        if (framesRead > part1) {
+        if ((size_t) framesRead > part1) {
             memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
                     (framesRead - part1) * mFrameSize);
         }
@@ -4953,6 +5156,11 @@
         for (size_t i = 0; i < size; i++) {
             activeTrack = activeTracks[i];
 
+            // skip fast tracks, as those are handled directly by FastCapture
+            if (activeTrack->isFastTrack()) {
+                continue;
+            }
+
             enum {
                 OVERRUN_UNKNOWN,
                 OVERRUN_TRUE,
@@ -5181,6 +5389,30 @@
 
 void AudioFlinger::RecordThread::inputStandBy()
 {
+    // Idle the fast capture if it's currently running
+    if (mFastCapture != 0) {
+        FastCaptureStateQueue *sq = mFastCapture->sq();
+        FastCaptureState *state = sq->begin();
+        if (!(state->mCommand & FastCaptureState::IDLE)) {
+            state->mCommand = FastCaptureState::COLD_IDLE;
+            state->mColdFutexAddr = &mFastCaptureFutex;
+            state->mColdGen++;
+            mFastCaptureFutex = 0;
+            sq->end();
+            // BLOCK_UNTIL_PUSHED would be insufficient, as we need it to stop doing I/O now
+            sq->push(FastCaptureStateQueue::BLOCK_UNTIL_ACKED);
+#if 0
+            if (kUseFastCapture == FastCapture_Dynamic) {
+                // FIXME
+            }
+#endif
+#ifdef AUDIO_WATCHDOG
+            // FIXME
+#endif
+        } else {
+            sq->end(false /*didModify*/);
+        }
+    }
     mInput->stream->common.standby(&mInput->stream->common);
 }
 
@@ -5207,36 +5439,40 @@
             // use case: callback handler and frame count is default or at least as large as HAL
             (
                 (tid != -1) &&
-                ((frameCount == 0) ||
+                ((frameCount == 0) /*||
+                // FIXME must be equal to pipe depth, so don't allow it to be specified by client
                 // FIXME not necessarily true, should be native frame count for native SR!
-                (frameCount >= mFrameCount))
+                (frameCount >= mFrameCount)*/)
             ) &&
             // PCM data
             audio_is_linear_pcm(format) &&
+            // native format
+            (format == mFormat) &&
             // mono or stereo
             ( (channelMask == AUDIO_CHANNEL_IN_MONO) ||
               (channelMask == AUDIO_CHANNEL_IN_STEREO) ) &&
-            // hardware sample rate
-            // FIXME actually the native hardware sample rate
+            // native channel mask
+            (channelMask == mChannelMask) &&
+            // native hardware sample rate
             (sampleRate == mSampleRate) &&
             // record thread has an associated fast capture
-            hasFastCapture()
-            // fast capture does not require slots
+            hasFastCapture() &&
+            // there are sufficient fast track slots available
+            mFastTrackAvail
         ) {
-        // if frameCount not specified, then it defaults to fast capture (HAL) frame count
+        // if frameCount not specified, then it defaults to pipe frame count
         if (frameCount == 0) {
-            // FIXME wrong mFrameCount
-            frameCount = mFrameCount * kFastTrackMultiplier;
+            frameCount = mPipeFramesP2;
         }
         ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
                 frameCount, mFrameCount);
       } else {
         ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d "
                 "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
-                "hasFastCapture=%d tid=%d",
+                "hasFastCapture=%d tid=%d mFastTrackAvail=%d",
                 frameCount, mFrameCount, format,
                 audio_is_linear_pcm(format),
-                channelMask, sampleRate, mSampleRate, hasFastCapture(), tid);
+                channelMask, sampleRate, mSampleRate, hasFastCapture(), tid, mFastTrackAvail);
         *flags &= ~IAudioFlinger::TRACK_FAST;
         // FIXME It's not clear that we need to enforce this any more, since we have a pipe.
         // For compatibility with AudioRecord calculation, buffer depth is forced
@@ -5465,6 +5701,10 @@
 {
     mTracks.remove(track);
     // need anything related to effects here?
+    if (track->isFastTrack()) {
+        ALOG_ASSERT(!mFastTrackAvail);
+        mFastTrackAvail = true;
+    }
 }
 
 void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
@@ -5483,6 +5723,7 @@
     } else {
         dprintf(fd, "  No active record clients\n");
     }
+    dprintf(fd, "  Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
 
     dumpBase(fd, args);
 }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 8c9943c..07887fb 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1064,6 +1064,8 @@
 
     virtual sp<MemoryDealer>    readOnlyHeap() const { return mReadOnlyHeap; }
 
+    virtual sp<IMemory> pipeMemory() const { return mPipeMemory; }
+
             sp<AudioFlinger::RecordThread::RecordTrack>  createRecordTrack_l(
                     const sp<AudioFlinger::Client>& client,
                     uint32_t sampleRate,
@@ -1115,7 +1117,7 @@
     static void syncStartEventCallback(const wp<SyncEvent>& event);
 
     virtual size_t      frameCount() const { return mFrameCount; }
-            bool        hasFastCapture() const { return false; }
+            bool        hasFastCapture() const { return mFastCapture != 0; }
 
 private:
             // Enter standby if not already in standby, and set mStandby flag
@@ -1145,4 +1147,40 @@
             const sp<NBAIO_Sink>                mTeeSink;
 
             const sp<MemoryDealer>              mReadOnlyHeap;
+
+            // one-time initialization, no locks required
+            sp<FastCapture>                     mFastCapture;   // non-0 if there is also a fast capture
+            // FIXME audio watchdog thread
+
+            // contents are not guaranteed to be consistent, no locks required
+            FastCaptureDumpState                mFastCaptureDumpState;
+#ifdef STATE_QUEUE_DUMP
+            // FIXME StateQueue observer and mutator dump fields
+#endif
+            // FIXME audio watchdog dump
+
+            // accessible only within the threadLoop(), no locks required
+            //          mFastCapture->sq()      // for mutating and pushing state
+            int32_t     mFastCaptureFutex;      // for cold idle
+
+            // The HAL input source is treated as non-blocking,
+            // but current implementation is blocking
+            sp<NBAIO_Source>                    mInputSource;
+            // The source for the normal capture thread to read from: mInputSource or mPipeSource
+            sp<NBAIO_Source>                    mNormalSource;
+            // If a fast capture is present, the non-blocking pipe sink written to by fast capture,
+            // otherwise clear
+            sp<NBAIO_Sink>                      mPipeSink;
+            // If a fast capture is present, the non-blocking pipe source read by normal thread,
+            // otherwise clear
+            sp<NBAIO_Source>                    mPipeSource;
+            // Depth of pipe from fast capture to normal thread and fast clients, always power of 2
+            size_t                              mPipeFramesP2;
+            // If a fast capture is present, the Pipe as IMemory, otherwise clear
+            sp<IMemory>                         mPipeMemory;
+
+            static const size_t                 kFastCaptureLogSize = 4 * 1024;
+            sp<NBLog::Writer>                   mFastCaptureNBLogWriter;
+
+            bool                                mFastTrackAvail;    // true if fast track available
 };
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 64ae0b0..8d5dc7b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1854,7 +1854,7 @@
     :   TrackBase(thread, client, sampleRate, format,
                   channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid,
                   flags, false /*isOut*/,
-                  (flags & IAudioFlinger::TRACK_FAST) != 0 ? ALLOC_READONLY : ALLOC_CBLK),
+                  flags & IAudioFlinger::TRACK_FAST ? ALLOC_PIPE : ALLOC_CBLK),
         mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0),
         // See real initialization of mRsmpInFront at RecordThread::start()
         mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL)
@@ -1876,6 +1876,11 @@
         mResampler->setVolume(AudioMixer::UNITY_GAIN_INT, AudioMixer::UNITY_GAIN_INT);
         mResamplerBufferProvider = new ResamplerBufferProvider(this);
     }
+
+    if (flags & IAudioFlinger::TRACK_FAST) {
+        ALOG_ASSERT(thread->mFastTrackAvail);
+        thread->mFastTrackAvail = false;
+    }
 }
 
 AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
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 7a3d2dc..7f3bdef 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,6 +285,7 @@
                             0);
         }
 
+        mpClientInterface->onAudioPortListUpdate();
         return NO_ERROR;
     }  // end if is output device
 
@@ -280,10 +293,8 @@
     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)
         {
@@ -293,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;
             }
@@ -300,6 +317,7 @@
             index = mAvailableInputDevices.add(devDesc);
             if (index >= 0) {
                 mAvailableInputDevices[index]->mId = nextUniqueId();
+                mAvailableInputDevices[index]->mModule = module;
             } else {
                 return NO_MEMORY;
             }
@@ -322,6 +340,7 @@
 
         closeAllInputs();
 
+        mpClientInterface->onAudioPortListUpdate();
         return NO_ERROR;
     } // end if is input device
 
@@ -334,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;
 
@@ -733,6 +751,7 @@
         }
         mPreviousOutputs = mOutputs;
         ALOGV("getOutput() returns new direct output %d", output);
+        mpClientInterface->onAudioPortListUpdate();
         return output;
     }
 
@@ -965,6 +984,7 @@
             if (dstOutput != mPrimaryOutput) {
                 mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
             }
+            mpClientInterface->onAudioPortListUpdate();
         }
     }
 }
@@ -1046,6 +1066,7 @@
         return 0;
     }
     addInput(input, inputDesc);
+    mpClientInterface->onAudioPortListUpdate();
     return input;
 }
 
@@ -1130,6 +1151,8 @@
     mpClientInterface->closeInput(input);
     delete mInputs.valueAt(index);
     mInputs.removeItem(input);
+    nextAudioPortGeneration();
+    mpClientInterface->onAudioPortListUpdate();
     ALOGV("releaseInput() exit");
 }
 
@@ -1138,6 +1161,7 @@
         mpClientInterface->closeInput(mInputs.keyAt(input_index));
     }
     mInputs.clear();
+    nextAudioPortGeneration();
 }
 
 void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream,
@@ -1458,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");
@@ -1587,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
 // ----------------------------------------------------------------------------
@@ -1596,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
@@ -1606,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");
@@ -1675,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 &&
@@ -1682,6 +2255,7 @@
                         mPrimaryOutput = output;
                     }
                     addOutput(output, outputDesc);
+                    ALOGI("CSTOR setOutputDevice %08x", outputDesc->mDevice);
                     setOutputDevice(output,
                                     outputDesc->mDevice,
                                     true);
@@ -1720,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);
@@ -1965,6 +2540,7 @@
     outputDesc->mIoHandle = output;
     outputDesc->mId = nextUniqueId();
     mOutputs.add(output, outputDesc);
+    nextAudioPortGeneration();
 }
 
 void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc)
@@ -1972,6 +2548,7 @@
     inputDesc->mIoHandle = input;
     inputDesc->mId = nextUniqueId();
     mInputs.add(input, inputDesc);
+    nextAudioPortGeneration();
 }
 
 String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address)
@@ -2144,6 +2721,7 @@
                                     mPrimaryOutput, output);
                             mpClientInterface->closeOutput(output);
                             mOutputs.removeItem(output);
+                            nextAudioPortGeneration();
                             output = 0;
                         }
                     }
@@ -2430,6 +3008,7 @@
     delete outputDesc;
     mOutputs.removeItem(output);
     mPreviousOutputs = mOutputs;
+    nextAudioPortGeneration();
 }
 
 SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
@@ -2582,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
@@ -2617,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);
@@ -2628,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;
 }
@@ -2982,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);
@@ -3026,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()) {
@@ -3036,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();
             }
         }
     }
@@ -3059,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;
 
@@ -3088,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;
 }
 
@@ -3715,6 +4397,7 @@
     return MAX_EFFECTS_MEMORY;
 }
 
+
 // --- AudioOutputDescriptor class implementation
 
 AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor(
@@ -3841,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(
@@ -3862,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;
@@ -3913,21 +4615,37 @@
 }
 
 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;
+        }
+    }
+    dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+    dstConfig->ext.mix.handle = mIoHandle;
+    dstConfig->ext.mix.usecase.source = mInputSource;
 }
 
 void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
@@ -3935,6 +4653,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;
 }
@@ -4039,6 +4759,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;
@@ -4066,6 +4920,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
@@ -4087,7 +4947,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;
 }
 
 
@@ -4110,7 +4976,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadFormats(char *name)
@@ -4133,7 +4998,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadInChannels(char *name)
@@ -4158,7 +5022,6 @@
         }
         str = strtok(NULL, "|");
     }
-    return;
 }
 
 void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
@@ -4187,10 +5050,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)
 {
 }
 
@@ -4254,42 +5281,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()
@@ -4394,10 +5395,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
 {
@@ -4415,6 +5439,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
 {
@@ -4430,51 +5468,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;
 }
@@ -4523,102 +5593,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;
             }
@@ -4630,13 +5628,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 {
@@ -4659,16 +5659,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) {
@@ -4676,13 +5682,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);
@@ -4704,9 +5711,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);
@@ -4720,13 +5727,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);
@@ -4734,7 +5741,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