Merge "Refactored SurfaceMediaSource"
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index 5cbfbfe..ece3c09 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -296,15 +296,28 @@
                     if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
                         CHECK(decryptInputBuffers);
 
-                        bufferFlags |= MediaCodec::BUFFER_FLAG_ENCRYPTED;
-                    }
+                        CryptoPlugin::SubSample ss;
+                        ss.mNumBytesOfClearData = 0;
+                        ss.mNumBytesOfEncryptedData = buffer->size();
 
-                    err = state->mCodec->queueInputBuffer(
-                            index,
-                            0 /* offset */,
-                            buffer->size(),
-                            timeUs,
-                            bufferFlags);
+                        err = state->mCodec->queueSecureInputBuffer(
+                                index,
+                                0 /* offset */,
+                                &ss,
+                                1 /* numSubSamples */,
+                                NULL /* key */,
+                                NULL /* iv */,
+                                CryptoPlugin::kMode_AES_WV,
+                                timeUs,
+                                bufferFlags);
+                    } else {
+                        err = state->mCodec->queueInputBuffer(
+                                index,
+                                0 /* offset */,
+                                buffer->size(),
+                                timeUs,
+                                bufferFlags);
+                    }
 
                     CHECK_EQ(err, (status_t)OK);
 
diff --git a/drm/libdrmframework/plugins/common/util/Android.mk b/drm/libdrmframework/plugins/common/util/Android.mk
index 81781b2..2a7b01f 100644
--- a/drm/libdrmframework/plugins/common/util/Android.mk
+++ b/drm/libdrmframework/plugins/common/util/Android.mk
@@ -22,13 +22,6 @@
 
 LOCAL_MODULE := libdrmutility
 
-LOCAL_SHARED_LIBRARIES :=  \
-    libutils \
-    libdl \
-    libdvm \
-    liblog
-
-
 base := frameworks/av
 
 LOCAL_C_INCLUDES += \
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 0b0d511..107699e 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -19,6 +19,7 @@
 #define MEDIA_CODEC_H_
 
 #include <gui/ISurfaceTexture.h>
+#include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/AHandler.h>
 #include <utils/Vector.h>
 
@@ -40,7 +41,6 @@
         BUFFER_FLAG_SYNCFRAME   = 1,
         BUFFER_FLAG_CODECCONFIG = 2,
         BUFFER_FLAG_EOS         = 4,
-        BUFFER_FLAG_ENCRYPTED   = 8,
     };
 
     static sp<MediaCodec> CreateByType(
@@ -74,6 +74,17 @@
             int64_t presentationTimeUs,
             uint32_t flags);
 
+    status_t queueSecureInputBuffer(
+            size_t index,
+            size_t offset,
+            const CryptoPlugin::SubSample *subSamples,
+            size_t numSubSamples,
+            const uint8_t key[16],
+            const uint8_t iv[16],
+            CryptoPlugin::Mode mode,
+            int64_t presentationTimeUs,
+            uint32_t flags);
+
     status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs = 0ll);
 
     status_t dequeueOutputBuffer(
diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf
index ce25bc8..d681c69 100644
--- a/media/libeffects/data/audio_effects.conf
+++ b/media/libeffects/data/audio_effects.conf
@@ -18,6 +18,9 @@
   pre_processing {
     path /system/lib/soundfx/libaudiopreprocessing.so
   }
+  downmix {
+    path /system/lib/soundfx/libdownmix.so
+  }
 }
 
 # list of effects to load. Each effect element must contain a "library" and a "uuid" element.
@@ -72,6 +75,10 @@
     library visualizer
     uuid d069d9e0-8329-11df-9168-0002a5d5c51b
   }
+  downmix {
+    library downmix
+    uuid 93f04452-e4fe-41cc-91f9-e475b6d1d69f
+  }
   agc {
     library pre_processing
     uuid aa8130e0-66fc-11e0-bad0-0002a5d5c51b
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
index a325172..302f1d7 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -610,7 +610,7 @@
         }
         value16 = *(int16_t *)pValue;
         ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
-        if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 < DOWNMIX_TYPE_LAST))) {
+        if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
             ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
             return -EINVAL;
         } else {
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index 1fe6bed..e6bea1f 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -97,6 +97,17 @@
         data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
         data.writeInt32(secure);
         data.writeInt32(mode);
+
+        static const uint8_t kDummy[16] = { 0 };
+
+        if (key == NULL) {
+            key = kDummy;
+        }
+
+        if (iv == NULL) {
+            iv = kDummy;
+        }
+
         data.write(key, 16);
         data.write(iv, 16);
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ced8368..a382f1c 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -191,6 +191,31 @@
     return PostAndAwaitResponse(msg, &response);
 }
 
+status_t MediaCodec::queueSecureInputBuffer(
+        size_t index,
+        size_t offset,
+        const CryptoPlugin::SubSample *subSamples,
+        size_t numSubSamples,
+        const uint8_t key[16],
+        const uint8_t iv[16],
+        CryptoPlugin::Mode mode,
+        int64_t presentationTimeUs,
+        uint32_t flags) {
+    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+    msg->setSize("index", index);
+    msg->setSize("offset", offset);
+    msg->setPointer("subSamples", (void *)subSamples);
+    msg->setSize("numSubSamples", numSubSamples);
+    msg->setPointer("key", (void *)key);
+    msg->setPointer("iv", (void *)iv);
+    msg->setInt32("mode", mode);
+    msg->setInt64("timeUs", presentationTimeUs);
+    msg->setInt32("flags", flags);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
     sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
     msg->setInt64("timeoutUs", timeoutUs);
@@ -1149,10 +1174,51 @@
     uint32_t flags;
     CHECK(msg->findSize("index", &index));
     CHECK(msg->findSize("offset", &offset));
-    CHECK(msg->findSize("size", &size));
     CHECK(msg->findInt64("timeUs", &timeUs));
     CHECK(msg->findInt32("flags", (int32_t *)&flags));
 
+    const CryptoPlugin::SubSample *subSamples;
+    size_t numSubSamples;
+    const uint8_t *key;
+    const uint8_t *iv;
+    CryptoPlugin::Mode mode = CryptoPlugin::kMode_Unencrypted;
+
+    // We allow the simpler queueInputBuffer API to be used even in
+    // secure mode, by fabricating a single unencrypted subSample.
+    CryptoPlugin::SubSample ss;
+
+    if (msg->findSize("size", &size)) {
+        if (mCrypto != NULL) {
+            ss.mNumBytesOfClearData = size;
+            ss.mNumBytesOfEncryptedData = 0;
+
+            subSamples = &ss;
+            numSubSamples = 1;
+            key = NULL;
+            iv = NULL;
+        }
+    } else {
+        if (mCrypto == NULL) {
+            return -EINVAL;
+        }
+
+        CHECK(msg->findPointer("subSamples", (void **)&subSamples));
+        CHECK(msg->findSize("numSubSamples", &numSubSamples));
+        CHECK(msg->findPointer("key", (void **)&key));
+        CHECK(msg->findPointer("iv", (void **)&iv));
+
+        int32_t tmp;
+        CHECK(msg->findInt32("mode", &tmp));
+
+        mode = (CryptoPlugin::Mode)tmp;
+
+        size = 0;
+        for (size_t i = 0; i < numSubSamples; ++i) {
+            size += subSamples[i].mNumBytesOfClearData;
+            size += subSamples[i].mNumBytesOfEncryptedData;
+        }
+    }
+
     if (index >= mPortBuffers[kPortIndexInput].size()) {
         return -ERANGE;
     }
@@ -1187,29 +1253,14 @@
             return -ERANGE;
         }
 
-        uint8_t key[16];
-        uint8_t iv[16];
-
-        CryptoPlugin::Mode mode;
-        CryptoPlugin::SubSample ss;
-        if (flags & BUFFER_FLAG_ENCRYPTED) {
-            mode = CryptoPlugin::kMode_AES_WV;
-            ss.mNumBytesOfClearData = 0;
-            ss.mNumBytesOfEncryptedData = size;
-        } else {
-            mode = CryptoPlugin::kMode_Unencrypted;
-            ss.mNumBytesOfClearData = size;
-            ss.mNumBytesOfEncryptedData = 0;
-        }
-
         status_t err = mCrypto->decrypt(
                 (mFlags & kFlagIsSecure) != 0,
                 key,
                 iv,
                 mode,
                 info->mEncryptedData->base() + offset,
-                &ss,
-                1 /* numSubSamples */,
+                subSamples,
+                numSubSamples,
                 info->mData->base());
 
         if (err != OK) {
@@ -1217,8 +1268,6 @@
         }
 
         info->mData->setRange(0, size);
-    } else if (flags & BUFFER_FLAG_ENCRYPTED) {
-        return -EINVAL;
     }
 
     reply->setBuffer("buffer", info->mData);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 9e6a6df..0ce1153 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2639,9 +2639,15 @@
 }
 
 // getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l()
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask)
 {
-    return mAudioMixer->getTrackName();
+    int name = mAudioMixer->getTrackName();
+    if (name >= 0) {
+        mAudioMixer->setParameter(name,
+                AudioMixer::TRACK,
+                AudioMixer::CHANNEL_MASK, (void *)channelMask);
+    }
+    return name;
 }
 
 // deleteTrackName_l() must be called with ThreadBase::mLock held
@@ -2738,7 +2744,7 @@
                 readOutputParameters();
                 mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
                 for (size_t i = 0; i < mTracks.size() ; i++) {
-                    int name = getTrackName_l();
+                    int name = getTrackName_l((audio_channel_mask_t)mTracks[i]->mChannelMask);
                     if (name < 0) break;
                     mTracks[i]->mName = name;
                     // limit track sample rate to 2 x new output sample rate
@@ -3066,7 +3072,7 @@
 }
 
 // getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l()
+int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask)
 {
     return 0;
 }
@@ -3528,7 +3534,7 @@
         // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
         mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t);
         // to avoid leaking a track name, do not allocate one unless there is an mCblk
-        mName = thread->getTrackName_l();
+        mName = thread->getTrackName_l((audio_channel_mask_t)channelMask);
         if (mName < 0) {
             ALOGE("no more track names available");
         }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index e493a9a..8a787e1 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -964,8 +964,9 @@
     protected:
         SortedVector< wp<Track> >       mActiveTracks;
 
-        // Allocate a track name.  Returns name >= 0 if successful, -1 on failure.
-        virtual int             getTrackName_l() = 0;
+        // Allocate a track name for a given channel mask.
+        //   Returns name >= 0 if successful, -1 on failure.
+        virtual int             getTrackName_l(audio_channel_mask_t channelMask) = 0;
         virtual void            deleteTrackName_l(int name) = 0;
 
         // Time to sleep between cycles when:
@@ -1057,7 +1058,7 @@
 
     protected:
         virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
-        virtual     int         getTrackName_l();
+        virtual     int         getTrackName_l(audio_channel_mask_t channelMask);
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    idleSleepTimeUs() const;
         virtual     uint32_t    suspendSleepTimeUs() const;
@@ -1082,7 +1083,7 @@
         virtual     bool        checkForNewParameters_l();
 
     protected:
-        virtual     int         getTrackName_l();
+        virtual     int         getTrackName_l(audio_channel_mask_t channelMask);
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs() const;
         virtual     uint32_t    idleSleepTimeUs() const;
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 0e6ea12..399d987 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -36,11 +36,62 @@
 #include <common_time/local_clock.h>
 #include <common_time/cc_helper.h>
 
+#include <media/EffectsFactoryApi.h>
+
 #include "AudioMixer.h"
 
 namespace android {
 
 // ----------------------------------------------------------------------------
+AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(),
+        mTrackBufferProvider(NULL), mDownmixHandle(NULL)
+{
+}
+
+AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
+{
+    ALOGV("AudioMixer deleting DownmixerBufferProvider (%p)", this);
+    EffectRelease(mDownmixHandle);
+}
+
+status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
+        int64_t pts) {
+    //ALOGV("DownmixerBufferProvider::getNextBuffer()");
+    if (this->mTrackBufferProvider != NULL) {
+        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+        if (res == OK) {
+            mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount;
+            mDownmixConfig.inputCfg.buffer.raw = pBuffer->raw;
+            mDownmixConfig.outputCfg.buffer.frameCount = pBuffer->frameCount;
+            mDownmixConfig.outputCfg.buffer.raw = mDownmixConfig.inputCfg.buffer.raw;
+            // in-place so overwrite the buffer contents, has been set in prepareTrackForDownmix()
+            //mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+
+            res = (*mDownmixHandle)->process(mDownmixHandle,
+                    &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
+            ALOGV("getNextBuffer is downmixing");
+        }
+        return res;
+    } else {
+        ALOGE("DownmixerBufferProvider::getNextBuffer() error: NULL track buffer provider");
+        return NO_INIT;
+    }
+}
+
+void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
+    ALOGV("DownmixerBufferProvider::releaseBuffer()");
+    if (this->mTrackBufferProvider != NULL) {
+        mTrackBufferProvider->releaseBuffer(pBuffer);
+    } else {
+        ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider");
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+bool AudioMixer::isMultichannelCapable = false;
+
+effect_descriptor_t AudioMixer::dwnmFxDesc;
 
 AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTracks)
     :   mTrackNames(0), mConfiguredNames((1 << maxNumTracks) - 1), mSampleRate(sampleRate)
@@ -70,6 +121,28 @@
         t->localTimeFreq = lc.getLocalFreq();
         t++;
     }
+
+    // find multichannel downmix effect if we have to play multichannel content
+    uint32_t numEffects = 0;
+    int ret = EffectQueryNumberEffects(&numEffects);
+    if (ret != 0) {
+        ALOGE("AudioMixer() error %d querying number of effects", ret);
+        return;
+    }
+    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
+
+    for (uint32_t i = 0 ; i < numEffects ; i++) {
+        if (EffectQueryEffect(i, &dwnmFxDesc) == 0) {
+            ALOGV("effect %d is called %s", i, dwnmFxDesc.name);
+            if (memcmp(&dwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
+                ALOGI("found effect \"%s\" from %s",
+                        dwnmFxDesc.name, dwnmFxDesc.implementor);
+                isMultichannelCapable = true;
+                break;
+            }
+        }
+    }
+    ALOGE_IF(!isMultichannelCapable, "unable to find downmix effect");
 }
 
 AudioMixer::~AudioMixer()
@@ -111,6 +184,7 @@
         t->channelMask = AUDIO_CHANNEL_OUT_STEREO;
         // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
         t->bufferProvider = NULL;
+        t->downmixerBufferProvider = NULL;
         t->buffer.raw = NULL;
         // no initialization needed
         // t->buffer.frameCount
@@ -135,6 +209,113 @@
     }
  }
 
+status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
+{
+    ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask);
+
+    if (pTrack->downmixerBufferProvider != NULL) {
+        // this track had previously been configured with a downmixer, reset it
+        ALOGV("AudioMixer::prepareTrackForDownmix(%d) deleting old downmixer", trackName);
+        pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider;
+        delete pTrack->downmixerBufferProvider;
+    }
+
+    DownmixerBufferProvider* pDbp = new DownmixerBufferProvider();
+    int32_t status;
+
+    if (!isMultichannelCapable) {
+        ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content",
+                trackName);
+        goto noDownmixForActiveTrack;
+    }
+
+    if (EffectCreate(&dwnmFxDesc.uuid,
+            -2 /*sessionId*/, -2 /*ioId*/,// both not relevant here, using random value
+            &pDbp->mDownmixHandle/*pHandle*/) != 0) {
+        ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName);
+        goto noDownmixForActiveTrack;
+    }
+
+    // channel input configuration will be overridden per-track
+    pDbp->mDownmixConfig.inputCfg.channels = pTrack->channelMask;
+    pDbp->mDownmixConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    pDbp->mDownmixConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    pDbp->mDownmixConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    pDbp->mDownmixConfig.inputCfg.samplingRate = pTrack->sampleRate;
+    pDbp->mDownmixConfig.outputCfg.samplingRate = pTrack->sampleRate;
+    pDbp->mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    pDbp->mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+    // input and output buffer provider, and frame count will not be used as the downmix effect
+    // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
+    pDbp->mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
+            EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
+    pDbp->mDownmixConfig.outputCfg.mask = pDbp->mDownmixConfig.inputCfg.mask;
+
+    {// scope for local variables that are not used in goto label "noDownmixForActiveTrack"
+        int cmdStatus;
+        uint32_t replySize = sizeof(int);
+
+        // Configure and enable downmixer
+        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
+                EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
+                &pDbp->mDownmixConfig /*pCmdData*/,
+                &replySize /*replySize*/, &cmdStatus /*pReplyData*/);
+        if ((status != 0) || (cmdStatus != 0)) {
+            ALOGE("error %d while configuring downmixer for track %d", status, trackName);
+            goto noDownmixForActiveTrack;
+        }
+        replySize = sizeof(int);
+        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
+                EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
+                &replySize /*replySize*/, &cmdStatus /*pReplyData*/);
+        if ((status != 0) || (cmdStatus != 0)) {
+            ALOGE("error %d while enabling downmixer for track %d", status, trackName);
+            goto noDownmixForActiveTrack;
+        }
+
+        // Set downmix type
+        // parameter size rounded for padding on 32bit boundary
+        const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
+        const int downmixParamSize =
+                sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
+        effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
+        param->psize = sizeof(downmix_params_t);
+        const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
+        memcpy(param->data, &downmixParam, param->psize);
+        const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
+        param->vsize = sizeof(downmix_type_t);
+        memcpy(param->data + psizePadded, &downmixType, param->vsize);
+
+        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
+                EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize/* cmdSize */,
+                param /*pCmndData*/, &replySize /*replySize*/, &cmdStatus /*pReplyData*/);
+
+        free(param);
+
+        if ((status != 0) || (cmdStatus != 0)) {
+            ALOGE("error %d while setting downmix type for track %d", status, trackName);
+            goto noDownmixForActiveTrack;
+        } else {
+            ALOGV("downmix type set to %d for track %d", (int) downmixType, trackName);
+        }
+    }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"
+
+    // initialization successful:
+    // - keep track of the real buffer provider in case it was set before
+    pDbp->mTrackBufferProvider = pTrack->bufferProvider;
+    // - we'll use the downmix effect integrated inside this
+    //    track's buffer provider, and we'll use it as the track's buffer provider
+    pTrack->downmixerBufferProvider = pDbp;
+    pTrack->bufferProvider = pDbp;
+
+    return NO_ERROR;
+
+noDownmixForActiveTrack:
+    delete pDbp;
+    pTrack->downmixerBufferProvider = NULL;
+    return NO_INIT;
+}
+
 void AudioMixer::deleteTrackName(int name)
 {
     name -= TRACK0;
@@ -177,6 +358,11 @@
     track_t& track = mState.tracks[name];
 
     if (track.enabled) {
+        if (track.downmixerBufferProvider != NULL) {
+            ALOGV("AudioMixer::disable(%d) deleting downmixerBufferProvider", name);
+            delete track.downmixerBufferProvider;
+            track.downmixerBufferProvider = NULL;
+        }
         track.enabled = false;
         ALOGV("disable(%d)", name);
         invalidateState(1 << name);
@@ -200,10 +386,14 @@
             uint32_t mask = (uint32_t)value;
             if (track.channelMask != mask) {
                 uint32_t channelCount = popcount(mask);
-                ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS) && (channelCount),
-                        "bad channel count %u", channelCount);
+                ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
                 track.channelMask = mask;
                 track.channelCount = channelCount;
+                if (channelCount > MAX_NUM_CHANNELS) {
+                    ALOGV("AudioMixer::setParameter(TRACK, CHANNEL_MASK, mask=0x%x count=%d)",
+                            mask, channelCount);
+                    status_t status = prepareTrackForDownmix(&mState.tracks[name], name);
+                }
                 ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask);
                 invalidateState(1 << name);
             }
@@ -225,6 +415,10 @@
         case FORMAT:
             ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT);
             break;
+        // FIXME do we want to support setting the downmix type from AudioFlinger?
+        //         for a specific track? or per mixer?
+        /* case DOWNMIX_TYPE:
+            break          */
         default:
             LOG_FATAL("bad param");
         }
@@ -350,7 +544,22 @@
 {
     name -= TRACK0;
     ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
-    mState.tracks[name].bufferProvider = bufferProvider;
+
+    if (mState.tracks[name].downmixerBufferProvider != NULL) {
+        // update required?
+        if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) {
+            ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider);
+            // setting the buffer provider for a track that gets downmixed consists in:
+            //  1/ setting the buffer provider to the "downmix / buffer provider" wrapper
+            //     so it's the one that gets called when the buffer provider is needed,
+            mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider;
+            //  2/ saving the buffer provider for the track so the wrapper can use it
+            //     when it downmixes.
+            mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider;
+        }
+    } else {
+        mState.tracks[name].bufferProvider = bufferProvider;
+    }
 }
 
 
@@ -419,13 +628,17 @@
                 all16BitsStereoNoResample = false;
                 resampling = true;
                 t.hook = track__genericResample;
+                ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
+                        "Track needs downmix + resample");
             } else {
                 if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                     t.hook = track__16BitsMono;
                     all16BitsStereoNoResample = false;
                 }
-                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){
+                if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
                     t.hook = track__16BitsStereo;
+                    ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
+                            "Track needs downmix");
                 }
             }
         }
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 856450c..a04fe95 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -21,9 +21,14 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <utils/threads.h>
+
 #include "AudioBufferProvider.h"
 #include "AudioResampler.h"
 
+#include <audio_effects/effect_downmix.h>
+#include <system/audio.h>
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -37,7 +42,10 @@
     /*virtual*/             ~AudioMixer();  // non-virtual saves a v-table, restore if sub-classed
 
     static const uint32_t MAX_NUM_TRACKS = 32;
+    // maximum number of channels supported by the mixer
     static const uint32_t MAX_NUM_CHANNELS = 2;
+    // maximum number of channels supported for the content
+    static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8;
 
     static const uint16_t UNITY_GAIN = 0x1000;
 
@@ -60,6 +68,7 @@
         FORMAT          = 0x4001,
         MAIN_BUFFER     = 0x4002,
         AUX_BUFFER      = 0x4003,
+        DOWNMIX_TYPE    = 0X4004,
         // for target RESAMPLE
         SAMPLE_RATE     = 0x4100,
         RESET           = 0x4101,
@@ -94,7 +103,7 @@
 private:
 
     enum {
-        NEEDS_CHANNEL_COUNT__MASK   = 0x00000003,
+        NEEDS_CHANNEL_COUNT__MASK   = 0x00000007,
         NEEDS_FORMAT__MASK          = 0x000000F0,
         NEEDS_MUTE__MASK            = 0x00000100,
         NEEDS_RESAMPLE__MASK        = 0x00001000,
@@ -119,6 +128,7 @@
 
     struct state_t;
     struct track_t;
+    class DownmixerBufferProvider;
 
     typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
     static const int BLOCKSIZE = 16; // 4 cache lines
@@ -147,8 +157,10 @@
         uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
         uint8_t     format;         // always 16
         uint16_t    enabled;        // actually bool
-        uint32_t    channelMask;    // currently under-used
+        audio_channel_mask_t channelMask;
 
+        // actual buffer provider used by the track hooks, see DownmixerBufferProvider below
+        //  for how the Track buffer provider is wrapped by another one when dowmixing is required
         AudioBufferProvider*                bufferProvider;
 
         // 16-byte boundary
@@ -169,7 +181,9 @@
 
         uint64_t    localTimeFreq;
 
-        int64_t     padding;
+        DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
+
+        int32_t     padding;
 
         // 16-byte boundary
 
@@ -194,6 +208,19 @@
         track_t         tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32)));
     };
 
+    // AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect
+    class DownmixerBufferProvider : public AudioBufferProvider {
+    public:
+        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+        virtual void releaseBuffer(Buffer* buffer);
+        DownmixerBufferProvider();
+        virtual ~DownmixerBufferProvider();
+
+        AudioBufferProvider* mTrackBufferProvider;
+        effect_handle_t    mDownmixHandle;
+        effect_config_t    mDownmixConfig;
+    };
+
     // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
     uint32_t        mTrackNames;
 
@@ -205,7 +232,13 @@
 
     state_t         mState __attribute__((aligned(32)));
 
+    // effect descriptor for the downmixer used by the mixer
+    static effect_descriptor_t dwnmFxDesc;
+    // indicates whether a downmix effect has been found and is usable by this mixer
+    static bool                isMultichannelCapable;
+
     void invalidateState(uint32_t mask);
+    static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
 
     static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
     static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);