Merge "Revert "Camera: Load initial session params speculatively"" into pi-dev
diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp
index 5d0f68e..ad1ccbc 100644
--- a/drm/libmediadrm/CryptoHal.cpp
+++ b/drm/libmediadrm/CryptoHal.cpp
@@ -262,6 +262,17 @@
 void CryptoHal::clearHeapBase(int32_t seqNum) {
     Mutex::Autolock autoLock(mLock);
 
+    /*
+     * Clear the remote shared memory mapping by setting the shared
+     * buffer base to a null hidl_memory.
+     *
+     * TODO: Add a releaseSharedBuffer method in a future DRM HAL
+     * API version to make this explicit.
+     */
+    uint32_t bufferId = mHeapBases.valueFor(seqNum).getBufferId();
+    Return<void> hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId);
+    ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed");
+
     mHeapBases.removeItem(seqNum);
 }
 
diff --git a/media/img_utils/src/DngUtils.cpp b/media/img_utils/src/DngUtils.cpp
index 67ec244..9ac7e2a 100644
--- a/media/img_utils/src/DngUtils.cpp
+++ b/media/img_utils/src/DngUtils.cpp
@@ -302,29 +302,14 @@
     normalizedOCX = CLAMP(normalizedOCX, 0, 1);
     normalizedOCY = CLAMP(normalizedOCY, 0, 1);
 
-    // Conversion factors from Camera2 K factors to DNG spec. K factors:
-    //
-    //      Note: these are necessary because our unit system assumes a
-    //      normalized max radius of sqrt(2), whereas the DNG spec's
-    //      WarpRectilinear opcode assumes a normalized max radius of 1.
-    //      Thus, each K coefficient must include the domain scaling
-    //      factor (the DNG domain is scaled by sqrt(2) to emulate the
-    //      domain used by the Camera2 specification).
-
-    const double c_0 = sqrt(2);
-    const double c_1 = 2 * sqrt(2);
-    const double c_2 = 4 * sqrt(2);
-    const double c_3 = 8 * sqrt(2);
-    const double c_4 = 2;
-    const double c_5 = 2;
-
-    const double coeffs[] = { c_0 * kCoeffs[0],
-                              c_1 * kCoeffs[1],
-                              c_2 * kCoeffs[2],
-                              c_3 * kCoeffs[3],
-                              c_4 * kCoeffs[4],
-                              c_5 * kCoeffs[5] };
-
+    double coeffs[6] = {
+        kCoeffs[0],
+        kCoeffs[1],
+        kCoeffs[2],
+        kCoeffs[3],
+        kCoeffs[4],
+        kCoeffs[5]
+    };
 
     return addWarpRectilinear(/*numPlanes*/1,
                               /*opticalCenterX*/normalizedOCX,
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index aa39443..f6f817a 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -192,7 +192,6 @@
     // always recompute for both channel masks even if only one has changed.
     const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
     const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
-    const bool mixerChannelCountChanged = track->mMixerChannelCount != mixerChannelCount;
 
     ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX)
             && trackChannelCount
@@ -213,7 +212,7 @@
     // do it after downmix since track format may change!
     track->prepareForReformat();
 
-    if (track->mResampler.get() != nullptr && mixerChannelCountChanged) {
+    if (track->mResampler.get() != nullptr) {
         // resampler channels may have changed.
         const uint32_t resetToSampleRate = track->sampleRate;
         track->mResampler.reset(nullptr);
diff --git a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
index 55383eb..0b883f1 100644
--- a/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
+++ b/media/libeffects/dynamicsproc/EffectDynamicsProcessing.cpp
@@ -51,7 +51,7 @@
         {0x7261676f, 0x6d75, 0x7369, 0x6364, {0x28, 0xe2, 0xfd, 0x3a, 0xc3, 0x9e}}, // type
         {0xe0e6539b, 0x1781, 0x7261, 0x676f, {0x6d, 0x75, 0x73, 0x69, 0x63, 0x40}}, // uuid
         EFFECT_CONTROL_API_VERSION,
-        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST | EFFECT_FLAG_VOLUME_CTRL),
         0, // TODO
         1,
         "Dynamics Processing",
@@ -367,6 +367,76 @@
     return 0;
 }
 
+//helper function
+bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
+        uint32_t expectedValues) {
+    if (paramSize < expectedParams * sizeof(int32_t)) {
+        ALOGE("Invalid paramSize: %u expected %u", paramSize,
+                (uint32_t)(expectedParams * sizeof(int32_t)));
+        return false;
+    }
+    if (valueSize < expectedValues * sizeof(int32_t)) {
+        ALOGE("Invalid valueSize %u expected %u", valueSize,
+                (uint32_t)(expectedValues * sizeof(int32_t)));
+        return false;
+    }
+    return true;
+}
+
+static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
+        int32_t channel) {
+    if (pContext->mPDynamics == NULL) {
+        return NULL;
+    }
+    dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
+    ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
+    return pChannel;
+}
+
+static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
+        int32_t eqType) {
+    dp_fx::DPChannel *pChannel = DP_getChannel(pContext, channel);
+    if (pChannel == NULL) {
+        return NULL;
+    }
+    dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
+            (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
+    ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
+    return pEq;
+}
+
+static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
+        int32_t eqType, int32_t band) {
+    dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
+    if (pEq == NULL) {
+        return NULL;
+    }
+    dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
+    ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
+    return pEqBand;
+}
+
+static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
+    dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
+    if (pChannel == NULL) {
+        return NULL;
+    }
+    dp_fx::DPMbc *pMbc = pChannel->getMbc();
+    ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
+    return pMbc;
+}
+
+static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
+        int32_t band) {
+    dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
+    if (pMbc == NULL) {
+        return NULL;
+    }
+    dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
+    ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
+    return pMbcBand;
+}
+
 int DP_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
         void *pCmdData, uint32_t *replySize, void *pReplyData) {
 
@@ -483,8 +553,49 @@
                 p->data + voffset);
         break;
     }
+    case EFFECT_CMD_SET_VOLUME: {
+        ALOGV("EFFECT_CMD_SET_VOLUME");
+        // if pReplyData is NULL, VOL_CTRL is delegated to another effect
+        if (pReplyData == NULL || replySize == NULL || *replySize < ((int)sizeof(int32_t) * 2)) {
+            ALOGV("no VOLUME data to return");
+            break;
+        }
+        if (pCmdData == NULL || cmdSize < ((int)sizeof(uint32_t) * 2)) {
+            ALOGE("\tLVM_ERROR : DynamicsProcessing EFFECT_CMD_SET_VOLUME ERROR");
+            return -EINVAL;
+        }
+
+        const int32_t unityGain = 1 << 24;
+        //channel count
+        int32_t channelCount = (int32_t)audio_channel_count_from_out_mask(
+                pContext->mConfig.inputCfg.channels);
+        for (int32_t ch = 0; ch < channelCount; ch++) {
+
+            dp_fx::DPChannel * pChannel = DP_getChannel(pContext, ch);
+            if (pChannel == NULL) {
+                ALOGE("%s EFFECT_CMD_SET_VOLUME invalid channel %d", __func__, ch);
+                return -EINVAL;
+                break;
+            }
+
+            int32_t offset = ch;
+            if (ch > 1) {
+                // FIXME: limited to 2 unique channels. If more channels present, use value for
+                // first channel
+                offset = 0;
+            }
+            const float gain = (float)*((uint32_t *)pCmdData + offset) / unityGain;
+            const float gainDb = linearToDb(gain);
+            ALOGVV("%s EFFECT_CMD_SET_VOLUME channel %d, engine outputlevel %f (%0.2f dB)",
+                    __func__, ch, gain, gainDb);
+            pChannel->setOutputGain(gainDb);
+        }
+
+        const int32_t  volRet[2] = {unityGain, unityGain}; // Apply no volume before effect.
+        memcpy(pReplyData, volRet, sizeof(volRet));
+        break;
+    }
     case EFFECT_CMD_SET_DEVICE:
-    case EFFECT_CMD_SET_VOLUME:
     case EFFECT_CMD_SET_AUDIO_MODE:
         break;
 
@@ -523,76 +634,6 @@
     return 0;
 }
 
-//helper function
-bool DP_checkSizesInt(uint32_t paramSize, uint32_t valueSize, uint32_t expectedParams,
-        uint32_t expectedValues) {
-    if (paramSize < expectedParams * sizeof(int32_t)) {
-        ALOGE("Invalid paramSize: %u expected %u", paramSize,
-                (uint32_t) (expectedParams * sizeof(int32_t)));
-        return false;
-    }
-    if (valueSize < expectedValues * sizeof(int32_t)) {
-        ALOGE("Invalid valueSize %u expected %u", valueSize,
-                (uint32_t)(expectedValues * sizeof(int32_t)));
-        return false;
-    }
-    return true;
-}
-
-static dp_fx::DPChannel* DP_getChannel(DynamicsProcessingContext *pContext,
-        int32_t channel) {
-    if (pContext->mPDynamics == NULL) {
-        return NULL;
-    }
-    dp_fx::DPChannel *pChannel = pContext->mPDynamics->getChannel(channel);
-    ALOGE_IF(pChannel == NULL, "DPChannel NULL. invalid channel %d", channel);
-    return pChannel;
-}
-
-static dp_fx::DPEq* DP_getEq(DynamicsProcessingContext *pContext, int32_t channel,
-        int32_t eqType) {
-    dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
-    if (pChannel == NULL) {
-        return NULL;
-    }
-    dp_fx::DPEq *pEq = (eqType == DP_PARAM_PRE_EQ ? pChannel->getPreEq() :
-            (eqType == DP_PARAM_POST_EQ ? pChannel->getPostEq() : NULL));
-    ALOGE_IF(pEq == NULL,"DPEq NULL invalid eq");
-    return pEq;
-}
-
-static dp_fx::DPEqBand* DP_getEqBand(DynamicsProcessingContext *pContext, int32_t channel,
-        int32_t eqType, int32_t band) {
-    dp_fx::DPEq *pEq = DP_getEq(pContext, channel, eqType);
-    if (pEq == NULL) {
-        return NULL;
-    }
-    dp_fx::DPEqBand *pEqBand = pEq->getBand(band);
-    ALOGE_IF(pEqBand == NULL, "DPEqBand NULL. invalid band %d", band);
-    return pEqBand;
-}
-
-static dp_fx::DPMbc* DP_getMbc(DynamicsProcessingContext *pContext, int32_t channel) {
-    dp_fx::DPChannel * pChannel = DP_getChannel(pContext, channel);
-    if (pChannel == NULL) {
-        return NULL;
-    }
-    dp_fx::DPMbc *pMbc = pChannel->getMbc();
-    ALOGE_IF(pMbc == NULL, "DPMbc NULL invalid MBC");
-    return pMbc;
-}
-
-static dp_fx::DPMbcBand* DP_getMbcBand(DynamicsProcessingContext *pContext, int32_t channel,
-        int32_t band) {
-    dp_fx::DPMbc *pMbc = DP_getMbc(pContext, channel);
-    if (pMbc == NULL) {
-        return NULL;
-    }
-    dp_fx::DPMbcBand *pMbcBand = pMbc->getBand(band);
-    ALOGE_IF(pMbcBand == NULL, "pMbcBand NULL. invalid band %d", band);
-    return pMbcBand;
-}
-
 int DP_getParameter(DynamicsProcessingContext *pContext,
                            uint32_t paramSize,
                            void *pParam,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.cpp b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
index 8b79991..ac758e0 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.cpp
@@ -174,8 +174,8 @@
 }
 
 //----
-DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mPreEqInUse(false), mMbcInUse(false),
-        mPostEqInUse(false), mLimiterInUse(false) {
+DPChannel::DPChannel() : mInitialized(false), mInputGainDb(0), mOutputGainDb(0),
+        mPreEqInUse(false), mMbcInUse(false), mPostEqInUse(false), mLimiterInUse(false) {
 }
 
 void DPChannel::init(float inputGain, bool preEqInUse, uint32_t preEqBandCount,
diff --git a/media/libeffects/dynamicsproc/dsp/DPBase.h b/media/libeffects/dynamicsproc/dsp/DPBase.h
index 355f64b..e74f91d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPBase.h
+++ b/media/libeffects/dynamicsproc/dsp/DPBase.h
@@ -272,6 +272,16 @@
         mInputGainDb = gain;
     }
 
+    float getOutputGain() const {
+        if (!mInitialized) {
+            return 0;
+        }
+        return mOutputGainDb;
+    }
+    void setOutputGain(float gain) {
+        mOutputGainDb = gain;
+    }
+
     DPEq* getPreEq();
     DPMbc* getMbc();
     DPEq* getPostEq();
@@ -281,6 +291,7 @@
 private:
     bool mInitialized;
     float mInputGainDb;
+    float mOutputGainDb;
 
     DPEq mPreEq;
     DPMbc mMbc;
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
index 59195fc..d06fd70 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.cpp
@@ -29,7 +29,7 @@
 
 #define CIRCULAR_BUFFER_UPSAMPLE 4  //4 times buffer size
 
-static constexpr float MIN_ENVELOPE = 0.000001f;
+static constexpr float MIN_ENVELOPE = 1e-6f; //-120 dB
 //helper functionS
 static inline bool isPowerOf2(unsigned long n) {
     return (n & (n - 1)) == 0;
@@ -53,14 +53,6 @@
 #define IS_CHANGED(c, a, b) { c |= !compareEquality(a,b); \
     (a) = (b); }
 
-float dBtoLinear(float valueDb) {
-    return  pow (10, valueDb / 20.0);
-}
-
-float linearToDb(float value) {
-    return 20 * log10(value);
-}
-
 //ChannelBuffers helper
 void ChannelBuffer::initBuffers(unsigned int blockSize, unsigned int overlapSize,
         unsigned int halfFftSize, unsigned int samplingRate, DPBase &dpBase) {
@@ -74,7 +66,7 @@
     cBOutput.resize(mBlockSize * CIRCULAR_BUFFER_UPSAMPLE);
 
     //fill input with half block size...
-    for (unsigned int k = 0;  k < mBlockSize/2; k++) {
+    for (unsigned int k = 0; k < mBlockSize/2; k++) {
         cBInput.write(0);
     }
 
@@ -94,12 +86,14 @@
             mMbcBands.size(), mPostEqBands.size());
 
     DPChannel *pChannel = dpBase.getChannel(0);
-    if (pChannel != NULL) {
+    if (pChannel != nullptr) {
         mPreEqInUse = pChannel->getPreEq()->isInUse();
         mMbcInUse = pChannel->getMbc()->isInUse();
         mPostEqInUse = pChannel->getPostEq()->isInUse();
         mLimiterInUse = pChannel->getLimiter()->isInUse();
     }
+
+    mLimiterParams.linkGroup = -1; //no group.
 }
 
 void ChannelBuffer::computeBinStartStop(BandParams &bp, size_t binStart) {
@@ -108,8 +102,35 @@
     bp.binStop = (int)(0.5 + bp.freqCutoffHz * mBlockSize / mSamplingRate);
 }
 
-//== DPFrequency
+//== LinkedLimiters Helper
+void LinkedLimiters::reset() {
+    mGroupsMap.clear();
+}
 
+void LinkedLimiters::update(int32_t group, int index) {
+    mGroupsMap[group].push_back(index);
+}
+
+void LinkedLimiters::remove(int index) {
+    //check all groups and if index is found, remove it.
+    //if group is empty afterwards, remove it.
+    for (auto it = mGroupsMap.begin(); it != mGroupsMap.end(); ) {
+        for (auto itIndex = it->second.begin(); itIndex != it->second.end(); ) {
+            if (*itIndex == index) {
+                itIndex = it->second.erase(itIndex);
+            } else {
+                ++itIndex;
+            }
+        }
+        if (it->second.size() == 0) {
+            it = mGroupsMap.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+//== DPFrequency
 void DPFrequency::reset() {
 }
 
@@ -147,14 +168,25 @@
                 mSamplingRate, *this);
     }
 
-    //dsp
+    //effective number of frames processed per second
+    mBlocksPerSecond = (float)mSamplingRate / (mBlockSize - mOverlapSize);
+
     fill_window(mVWindow, RDSP_WINDOW_HANNING_FLAT_TOP, mBlockSize, mOverlapSize);
+
+    //compute window rms for energy compensation
+    mWindowRms = 0;
+    for (size_t i = 0; i < mVWindow.size(); i++) {
+        mWindowRms += mVWindow[i] * mVWindow[i];
+    }
+
+    //Making sure window rms is not zero.
+    mWindowRms = std::max(sqrt(mWindowRms / mVWindow.size()), MIN_ENVELOPE);
 }
 
 void DPFrequency::updateParameters(ChannelBuffer &cb, int channelIndex) {
     DPChannel *pChannel = getChannel(channelIndex);
 
-    if (pChannel == NULL) {
+    if (pChannel == nullptr) {
         ALOGE("Error: updateParameters null DPChannel %d", channelIndex);
         return;
     }
@@ -166,7 +198,7 @@
         //===EqPre
         if (cb.mPreEqInUse) {
             DPEq *pPreEq = pChannel->getPreEq();
-            if (pPreEq == NULL) {
+            if (pPreEq == nullptr) {
                 ALOGE("Error: updateParameters null PreEq for channel: %d", channelIndex);
                 return;
             }
@@ -174,7 +206,7 @@
             if (cb.mPreEqEnabled) {
                 for (unsigned int b = 0; b < getPreEqBandCount(); b++) {
                     DPEqBand *pEqBand = pPreEq->getBand(b);
-                    if (pEqBand == NULL) {
+                    if (pEqBand == nullptr) {
                         ALOGE("Error: updateParameters null PreEqBand for band %d", b);
                         return; //failed.
                     }
@@ -222,7 +254,7 @@
         bool changed = false;
 
         DPEq *pPostEq = pChannel->getPostEq();
-        if (pPostEq == NULL) {
+        if (pPostEq == nullptr) {
             ALOGE("Error: updateParameters null postEq for channel: %d", channelIndex);
             return; //failed.
         }
@@ -230,7 +262,7 @@
         if (cb.mPostEqEnabled) {
             for (unsigned int b = 0; b < getPostEqBandCount(); b++) {
                 DPEqBand *pEqBand = pPostEq->getBand(b);
-                if (pEqBand == NULL) {
+                if (pEqBand == nullptr) {
                     ALOGE("Error: updateParameters PostEqBand NULL for band %d", b);
                     return; //failed.
                 }
@@ -265,7 +297,7 @@
     //===MBC
     if (cb.mMbcInUse) {
         DPMbc *pMbc = pChannel->getMbc();
-        if (pMbc == NULL) {
+        if (pMbc == nullptr) {
             ALOGE("Error: updateParameters Mbc NULL for channel: %d", channelIndex);
             return;
         }
@@ -274,7 +306,7 @@
             bool changed = false;
             for (unsigned int b = 0; b < getMbcBandCount(); b++) {
                 DPMbcBand *pMbcBand = pMbc->getBand(b);
-                if (pMbcBand == NULL) {
+                if (pMbcBand == nullptr) {
                     ALOGE("Error: updateParameters MbcBand NULL for band %d", b);
                     return; //failed.
                 }
@@ -307,11 +339,38 @@
                     cb.computeBinStartStop(*pMbcBandParams, binNext);
                     binNext = pMbcBandParams->binStop + 1;
                 }
-
             }
-
         }
     }
+
+    //===Limiter
+    if (cb.mLimiterInUse) {
+        bool changed = false;
+        DPLimiter *pLimiter = pChannel->getLimiter();
+        if (pLimiter == nullptr) {
+            ALOGE("Error: updateParameters Limiter NULL for channel: %d", channelIndex);
+            return;
+        }
+        cb.mLimiterEnabled = pLimiter->isEnabled();
+        if (cb.mLimiterEnabled) {
+            IS_CHANGED(changed, cb.mLimiterParams.linkGroup ,
+                    (int32_t)pLimiter->getLinkGroup());
+            cb.mLimiterParams.attackTimeMs = pLimiter->getAttackTime();
+            cb.mLimiterParams.releaseTimeMs = pLimiter->getReleaseTime();
+            cb.mLimiterParams.ratio = pLimiter->getRatio();
+            cb.mLimiterParams.thresholdDb = pLimiter->getThreshold();
+            cb.mLimiterParams.postGainDb = pLimiter->getPostGain();
+        }
+
+        if (changed) {
+            ALOGV("limiter changed, recomputing linkGroups for %d", channelIndex);
+            mLinkedLimiters.remove(channelIndex); //in case it was already there.
+            mLinkedLimiters.update(cb.mLimiterParams.linkGroup, channelIndex);
+        }
+    }
+
+    //=== Output Gain
+    cb.outputGainDb = pChannel->getOutputGain();
 }
 
 size_t DPFrequency::processSamples(const float *in, float *out, size_t samples) {
@@ -336,12 +395,8 @@
            }
        }
 
-       //TODO: lookahead limiters
-       //TODO: apply linked limiters to all channels.
-       //**Process each Channel
-       for (int ch = 0; ch < channelCount; ch++) {
-           processMono(mChannelBuffers[ch]);
-       }
+       //**process all channelBuffers
+       processChannelBuffers(mChannelBuffers);
 
        //** estimate how much data is available in ALL channels
        size_t available = mChannelBuffers[0].cBOutput.availableToRead();
@@ -370,62 +425,78 @@
        return samples;
 }
 
-size_t DPFrequency::processMono(ChannelBuffer &cb) {
-
+size_t DPFrequency::processChannelBuffers(CBufferVector &channelBuffers) {
+    const int channelCount = channelBuffers.size();
     size_t processedSamples = 0;
+    size_t processFrames = mBlockSize - mOverlapSize;
 
-    size_t available = cb.cBInput.availableToRead();
-    while (available >= mBlockSize - mOverlapSize) {
-
-        //move tail of previous
-        for (unsigned int k = 0; k < mOverlapSize; ++k) {
-            cb.input[k] = cb.input[mBlockSize - mOverlapSize + k];
-        }
-
-        //read new available data
-        for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
-            cb.input[mOverlapSize + k] = cb.cBInput.read();
-        }
-
-        //## Actual process
-        processOneVector(cb.output, cb.input, cb);
-        //##End of Process
-
-        //mix tail (and capture new tail
-        for (unsigned int k = 0; k < mOverlapSize; k++) {
-            cb.output[k] += cb.outTail[k];
-            cb.outTail[k] = cb.output[mBlockSize - mOverlapSize + k]; //new tail
-        }
-
-        //output data
-        for (unsigned int k = 0; k < mBlockSize - mOverlapSize; k++) {
-            cb.cBOutput.write(cb.output[k]);
-        }
-
-        available = cb.cBInput.availableToRead();
+    size_t available = channelBuffers[0].cBInput.availableToRead();
+    for (int ch = 1; ch < channelCount; ch++) {
+        available = std::min(available, channelBuffers[ch].cBInput.availableToRead());
     }
 
+    while (available >= processFrames) {
+        //First pass
+        for (int ch = 0; ch < channelCount; ch++) {
+            ChannelBuffer * pCb = &channelBuffers[ch];
+            //move tail of previous
+            std::copy(pCb->input.begin() + processFrames,
+                    pCb->input.end(),
+                    pCb->input.begin());
+
+            //read new available data
+            for (unsigned int k = 0; k < processFrames; k++) {
+                pCb->input[mOverlapSize + k] = pCb->cBInput.read();
+            }
+            //first stages: fft, preEq, mbc, postEq and start of Limiter
+            processedSamples += processFirstStages(*pCb);
+        }
+
+        //**compute linked limiters and update levels if needed
+        processLinkedLimiters(channelBuffers);
+
+        //final pass.
+        for (int ch = 0; ch < channelCount; ch++) {
+            ChannelBuffer * pCb = &channelBuffers[ch];
+
+            //linked limiter and ifft
+            processLastStages(*pCb);
+
+            //mix tail (and capture new tail
+            for (unsigned int k = 0; k < mOverlapSize; k++) {
+                pCb->output[k] += pCb->outTail[k];
+                pCb->outTail[k] = pCb->output[processFrames + k]; //new tail
+            }
+
+            //output data
+            for (unsigned int k = 0; k < processFrames; k++) {
+                pCb->cBOutput.write(pCb->output[k]);
+            }
+        }
+        available -= processFrames;
+    }
     return processedSamples;
 }
-
-size_t DPFrequency::processOneVector(FloatVec & output, FloatVec & input,
-        ChannelBuffer &cb) {
+size_t DPFrequency::processFirstStages(ChannelBuffer &cb) {
 
     //##apply window
     Eigen::Map<Eigen::VectorXf> eWindow(&mVWindow[0], mVWindow.size());
-    Eigen::Map<Eigen::VectorXf> eInput(&input[0], input.size());
+    Eigen::Map<Eigen::VectorXf> eInput(&cb.input[0], cb.input.size());
 
     Eigen::VectorXf eWin = eInput.cwiseProduct(eWindow); //apply window
 
-    //##fft //TODO: refactor frequency transformations away from other stages.
-    mFftServer.fwd(mComplexTemp, eWin);
+    //##fft
+    //Note: we are using eigen with the default scaling, which ensures that
+    //  IFFT( FFT(x) ) = x.
+    // TODO: optimize by using the noscale option, and compensate with dB scale offsets
+    mFftServer.fwd(cb.complexTemp, eWin);
 
-    size_t cSize = mComplexTemp.size();
+    size_t cSize = cb.complexTemp.size();
     size_t maxBin = std::min(cSize/2, mHalfFFTSize);
 
     //== EqPre (always runs)
     for (size_t k = 0; k < maxBin; k++) {
-        mComplexTemp[k] *= cb.mPreEqFactorVector[k];
+        cb.complexTemp[k] *= cb.mPreEqFactorVector[k];
     }
 
     //== MBC
@@ -439,62 +510,68 @@
             float preGainSquared = preGainFactor * preGainFactor;
 
             for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
-                float fReal = mComplexTemp[k].real();
-                float fImag = mComplexTemp[k].imag();
-                float fSquare = (fReal * fReal + fImag * fImag) * preGainSquared;
-
-                fEnergySum += fSquare;
+                fEnergySum += std::norm(cb.complexTemp[k]) * preGainSquared; //mag squared
             }
 
-            fEnergySum = sqrt(fEnergySum /2.0);
+            //Eigen FFT is full spectrum, even if the source was real data.
+            // Each half spectrum has half the energy. This is taken into account with the * 2
+            // factor in the energy computations.
+            // energy = sqrt(sum_components_squared) number_points
+            // in here, the fEnergySum is duplicated to account for the second half spectrum,
+            // and the windowRms is used to normalize by the expected energy reduction
+            // caused by the window used (expected for steady state signals)
+            fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+
+            // updates computed per frame advance.
             float fTheta = 0.0;
-            float fFAtt = pMbcBandParams->attackTimeMs;
-            float fFRel = pMbcBandParams->releaseTimeMs;
-
-            float fUpdatesPerSecond = 10; //TODO: compute from framerate
-
+            float fFAttSec = pMbcBandParams->attackTimeMs / 1000; //in seconds
+            float fFRelSec = pMbcBandParams->releaseTimeMs / 1000; //in seconds
 
             if (fEnergySum > pMbcBandParams->previousEnvelope) {
-                fTheta = exp(-1.0 / (fFAtt * fUpdatesPerSecond));
+                fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
             } else {
-                fTheta = exp(-1.0 / (fFRel * fUpdatesPerSecond));
+                fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
             }
 
             float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * pMbcBandParams->previousEnvelope;
-
             //preserve for next iteration
             pMbcBandParams->previousEnvelope = fEnv;
 
-            float fThreshold = dBtoLinear(pMbcBandParams->thresholdDb);
-            float fNoiseGateThreshold = dBtoLinear(pMbcBandParams->noiseGateThresholdDb);
-
-            float fNewFactor = 1.0;
-
-            if (fEnv > fThreshold) {
-                float fDbAbove = linearToDb(fThreshold / fEnv);
-                float fDbTarget = fDbAbove / pMbcBandParams->ratio;
-                float fDbChange = fDbAbove - fDbTarget;
-                fNewFactor = dBtoLinear(fDbChange);
-            } else if (fEnv < fNoiseGateThreshold) {
-                if (fEnv < MIN_ENVELOPE) {
-                    fEnv = MIN_ENVELOPE;
-                }
-                float fDbBelow = linearToDb(fNoiseGateThreshold / fEnv);
-                float fDbTarget = fDbBelow / pMbcBandParams->expanderRatio;
-                float fDbChange = fDbBelow - fDbTarget;
-                fNewFactor = dBtoLinear(fDbChange);
+            if (fEnv < MIN_ENVELOPE) {
+                fEnv = MIN_ENVELOPE;
             }
+            const float envDb = linearToDb(fEnv);
+            float newLevelDb = envDb;
+            //using shorter variables for code clarity
+            const float thresholdDb = pMbcBandParams->thresholdDb;
+            const float ratio = pMbcBandParams->ratio;
+            const float kneeWidthDbHalf = pMbcBandParams->kneeWidthDb / 2;
+            const float noiseGateThresholdDb = pMbcBandParams->noiseGateThresholdDb;
+            const float expanderRatio = pMbcBandParams->expanderRatio;
+
+            //find segment
+            if (envDb > thresholdDb + kneeWidthDbHalf) {
+                //compression segment
+                newLevelDb = envDb + ((1 / ratio) - 1) * (envDb - thresholdDb);
+            } else if (envDb > thresholdDb - kneeWidthDbHalf) {
+                //knee-compression segment
+                float temp = (envDb - thresholdDb + kneeWidthDbHalf);
+                newLevelDb = envDb + ((1 / ratio) - 1) *
+                        temp * temp / (kneeWidthDbHalf * 4);
+            } else if (envDb < noiseGateThresholdDb) {
+                //expander segment
+                newLevelDb = noiseGateThresholdDb -
+                        expanderRatio * (noiseGateThresholdDb - envDb);
+            }
+
+            float newFactor = dBtoLinear(newLevelDb - envDb);
 
             //apply post gain.
-            fNewFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
-
-            if (fNewFactor < 0) {
-                fNewFactor = 0;
-            }
+            newFactor *= dBtoLinear(pMbcBandParams->gainPostDb);
 
             //apply to this band
             for (size_t k = pMbcBandParams->binStart; k <= pMbcBandParams->binStop; k++) {
-                mComplexTemp[k] *= fNewFactor;
+                cb.complexTemp[k] *= newFactor;
             }
 
         } //end per band process
@@ -504,14 +581,94 @@
     //== EqPost
     if (cb.mPostEqInUse && cb.mPostEqEnabled) {
         for (size_t k = 0; k < maxBin; k++) {
-            mComplexTemp[k] *= cb.mPostEqFactorVector[k];
+            cb.complexTemp[k] *= cb.mPostEqFactorVector[k];
+        }
+    }
+
+    //== Limiter. First Pass
+    if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+        float fEnergySum = 0;
+        for (size_t k = 0; k < maxBin; k++) {
+            fEnergySum += std::norm(cb.complexTemp[k]);
+        }
+
+        //see explanation above for energy computation logic
+        fEnergySum = sqrt(fEnergySum * 2) / (mBlockSize * mWindowRms);
+        float fTheta = 0.0;
+        float fFAttSec = cb.mLimiterParams.attackTimeMs / 1000; //in seconds
+        float fFRelSec = cb.mLimiterParams.releaseTimeMs / 1000; //in seconds
+
+        if (fEnergySum > cb.mLimiterParams.previousEnvelope) {
+            fTheta = exp(-1.0 / (fFAttSec * mBlocksPerSecond));
+        } else {
+            fTheta = exp(-1.0 / (fFRelSec * mBlocksPerSecond));
+        }
+
+        float fEnv = (1.0 - fTheta) * fEnergySum + fTheta * cb.mLimiterParams.previousEnvelope;
+        //preserve for next iteration
+        cb.mLimiterParams.previousEnvelope = fEnv;
+
+        const float envDb = linearToDb(fEnv);
+        float newFactorDb = 0;
+        //using shorter variables for code clarity
+        const float thresholdDb = cb.mLimiterParams.thresholdDb;
+        const float ratio = cb.mLimiterParams.ratio;
+
+        if (envDb > thresholdDb) {
+            //limiter segment
+            newFactorDb = ((1 / ratio) - 1) * (envDb - thresholdDb);
+        }
+
+        float newFactor = dBtoLinear(newFactorDb);
+
+        cb.mLimiterParams.newFactor = newFactor;
+
+    } //end Limiter
+    return mBlockSize;
+}
+
+void DPFrequency::processLinkedLimiters(CBufferVector &channelBuffers) {
+
+    const int channelCount = channelBuffers.size();
+    for (auto &groupPair : mLinkedLimiters.mGroupsMap) {
+        float minFactor = 1.0;
+        //estimate minfactor for all linked
+        for(int index : groupPair.second) {
+            if (index >= 0 && index < channelCount) {
+                minFactor = std::min(channelBuffers[index].mLimiterParams.newFactor, minFactor);
+            }
+        }
+        //apply minFactor
+        for(int index : groupPair.second) {
+            if (index >= 0 && index < channelCount) {
+                channelBuffers[index].mLimiterParams.linkFactor = minFactor;
+            }
+        }
+    }
+}
+
+size_t DPFrequency::processLastStages(ChannelBuffer &cb) {
+
+    float outputGainFactor = dBtoLinear(cb.outputGainDb);
+    //== Limiter. last Pass
+    if (cb.mLimiterInUse && cb.mLimiterEnabled) {
+        //compute factor, with post-gain
+        float factor = cb.mLimiterParams.linkFactor * dBtoLinear(cb.mLimiterParams.postGainDb);
+        outputGainFactor *= factor;
+    }
+
+    //apply to all if != 1.0
+    if (!compareEquality(outputGainFactor, 1.0f)) {
+        size_t cSize = cb.complexTemp.size();
+        size_t maxBin = std::min(cSize/2, mHalfFFTSize);
+        for (size_t k = 0; k < maxBin; k++) {
+            cb.complexTemp[k] *= outputGainFactor;
         }
     }
 
     //##ifft directly to output.
-    Eigen::Map<Eigen::VectorXf> eOutput(&output[0], output.size());
-    mFftServer.inv(eOutput, mComplexTemp);
-
+    Eigen::Map<Eigen::VectorXf> eOutput(&cb.output[0], cb.output.size());
+    mFftServer.inv(eOutput, cb.complexTemp);
     return mBlockSize;
 }
 
diff --git a/media/libeffects/dynamicsproc/dsp/DPFrequency.h b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
index 9919142..be8771d 100644
--- a/media/libeffects/dynamicsproc/dsp/DPFrequency.h
+++ b/media/libeffects/dynamicsproc/dsp/DPFrequency.h
@@ -39,8 +39,11 @@
     FloatVec output;    // time domain temp vector for output
     FloatVec outTail;   // time domain temp vector for output tail (for overlap-add method)
 
+    Eigen::VectorXcf complexTemp; // complex temp vector for frequency domain operations
+
     //Current parameters
     float inputGainDb;
+    float outputGainDb;
     struct BandParams {
         bool enabled;
         float freqCutoffHz;
@@ -64,6 +67,19 @@
         //Historic values
         float previousEnvelope;
     };
+    struct LimiterParams {
+        int32_t linkGroup;
+        float attackTimeMs;
+        float releaseTimeMs;
+        float ratio;
+        float thresholdDb;
+        float postGainDb;
+
+        //Historic values
+        float previousEnvelope;
+        float newFactor;
+        float linkFactor;
+    };
 
     bool mPreEqInUse;
     bool mPreEqEnabled;
@@ -79,6 +95,7 @@
 
     bool mLimiterInUse;
     bool mLimiterEnabled;
+    LimiterParams mLimiterParams;
     FloatVec mPreEqFactorVector; // temp pre-computed vector to shape spectrum at preEQ stage
     FloatVec mPostEqFactorVector; // temp pre-computed vector to shape spectrum at postEQ stage
 
@@ -91,6 +108,18 @@
 
 };
 
+using CBufferVector = std::vector<ChannelBuffer>;
+
+using GroupsMap = std::map<int32_t, IntVec>;
+
+class LinkedLimiters {
+public:
+    void reset();
+    void update(int32_t group, int index);
+    void remove(int index);
+    GroupsMap mGroupsMap;
+};
+
 class DPFrequency : public DPBase {
 public:
     virtual size_t processSamples(const float *in, float *out, size_t samples);
@@ -104,16 +133,25 @@
     size_t processMono(ChannelBuffer &cb);
     size_t processOneVector(FloatVec &output, FloatVec &input, ChannelBuffer &cb);
 
+    size_t processChannelBuffers(CBufferVector &channelBuffers);
+    size_t processFirstStages(ChannelBuffer &cb);
+    size_t processLastStages(ChannelBuffer &cb);
+    void processLinkedLimiters(CBufferVector &channelBuffers);
+
     size_t mBlockSize;
     size_t mHalfFFTSize;
     size_t mOverlapSize;
     size_t mSamplingRate;
 
-    std::vector<ChannelBuffer> mChannelBuffers;
+    float mBlocksPerSecond;
+
+    CBufferVector mChannelBuffers;
+
+    LinkedLimiters mLinkedLimiters;
 
     //dsp
     FloatVec mVWindow;  //window class.
-    Eigen::VectorXcf mComplexTemp;
+    float mWindowRms;
     Eigen::FFT<float> mFftServer;
 };
 
diff --git a/media/libeffects/dynamicsproc/dsp/RDsp.h b/media/libeffects/dynamicsproc/dsp/RDsp.h
index 1048442..cfa1305 100644
--- a/media/libeffects/dynamicsproc/dsp/RDsp.h
+++ b/media/libeffects/dynamicsproc/dsp/RDsp.h
@@ -20,10 +20,25 @@
 #include <complex>
 #include <log/log.h>
 #include <vector>
+#include <map>
 using FloatVec = std::vector<float>;
+using IntVec = std::vector<int>;
 using ComplexVec  = std::vector<std::complex<float>>;
 
 // =======
+// Helper Functions
+// =======
+template <class T>
+static T dBtoLinear(T valueDb) {
+    return pow (10, valueDb / 20.0);
+}
+
+template <class T>
+static T linearToDb(T value) {
+    return 20 * log10(value);
+}
+
+// =======
 // DSP window creation
 // =======
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index cbc3015..23d66bb 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -934,7 +934,11 @@
     sp<MetaData> meta = mSources.itemAt(trackIndex)->getFormat();
     if (meta == NULL) {
         ALOGE("no metadata for track %zu", trackIndex);
-        return NULL;
+        format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+        format->setString("mime", "application/octet-stream");
+        format->setString("language", "und");
+
+        return format;
     }
 
     const char *mime;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 0402fca..fb12360 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -155,7 +155,9 @@
             break;
         default:
             ALOGE("Unknown track type: %d", track.mTrackType);
-            return NULL;
+            format->setInt32("type", MEDIA_TRACK_TYPE_UNKNOWN);
+            format->setString("mime", "application/octet-stream");
+            return format;
     }
 
     // For CEA-608 CC1, field 0 channel 0
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 2a08f62..69cd82e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -745,6 +745,7 @@
     sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
     reply->setSize("buffer-ix", index);
     reply->setInt32("generation", mBufferGeneration);
+    reply->setSize("size", size);
 
     if (eos) {
         ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
@@ -1127,6 +1128,7 @@
     int32_t render;
     size_t bufferIx;
     int32_t eos;
+    size_t size;
     CHECK(msg->findSize("buffer-ix", &bufferIx));
 
     if (!mIsAudio) {
@@ -1146,7 +1148,10 @@
         CHECK(msg->findInt64("timestampNs", &timestampNs));
         err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
     } else {
-        mNumOutputFramesDropped += !mIsAudio;
+        if (!msg->findInt32("eos", &eos) || !eos ||
+                !msg->findSize("size", &size) || size) {
+            mNumOutputFramesDropped += !mIsAudio;
+        }
         err = mCodec->releaseOutputBuffer(bufferIx);
     }
     if (err != OK) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2abea9e..bf5ae2c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5322,13 +5322,13 @@
     convertCodecColorAspectsToPlatformAspects(aspects, &range, &standard, &transfer);
 
     // if some aspects are unspecified, use dataspace fields
-    if (range != 0) {
+    if (range == 0) {
         range = (dataSpace & HAL_DATASPACE_RANGE_MASK) >> HAL_DATASPACE_RANGE_SHIFT;
     }
-    if (standard != 0) {
+    if (standard == 0) {
         standard = (dataSpace & HAL_DATASPACE_STANDARD_MASK) >> HAL_DATASPACE_STANDARD_SHIFT;
     }
-    if (transfer != 0) {
+    if (transfer == 0) {
         transfer = (dataSpace & HAL_DATASPACE_TRANSFER_MASK) >> HAL_DATASPACE_TRANSFER_SHIFT;
     }
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 06a49d0..8436591 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -854,8 +854,7 @@
 
 //static
 sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
-    static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
-    if (ccodecEnabled && name.startsWithIgnoreCase("c2.")) {
+    if (name.startsWithIgnoreCase("c2.")) {
         return CreateCCodec();
     } else if (name.startsWithIgnoreCase("omx.")) {
         // at this time only ACodec specifies a mime type.
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 9a33168..8598a49 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -93,9 +93,7 @@
 
 std::vector<MediaCodecListBuilderBase *> GetBuilders() {
     std::vector<MediaCodecListBuilderBase *> builders {&sOmxInfoBuilder};
-    if (property_get_bool("debug.stagefright.ccodec", false)) {
-        builders.push_back(GetCodec2InfoBuilder());
-    }
+    builders.push_back(GetCodec2InfoBuilder());
     return builders;
 }
 
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 43304aa..df66ac6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -197,6 +197,7 @@
         CHECK(mNumItems < kMaxNumItems);
         i = mNumItems++;
         item = &mItems[i];
+        item->mType = kTypeInt32;
         item->setName(name, len);
     }
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
index fac6cbe..32b4440 100644
--- a/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
+++ b/services/audiopolicy/common/managerdefinitions/include/SessionRoute.h
@@ -24,6 +24,7 @@
 namespace android {
 
 class DeviceDescriptor;
+class DeviceVector;
 
 class SessionRoute : public RefBase
 {
@@ -98,7 +99,8 @@
     int decRouteActivity(audio_session_t session);
     bool getAndClearRouteChanged(audio_session_t session); // also clears the changed flag
     void log(const char* caption);
-
+    audio_devices_t getActiveDeviceForStream(audio_stream_type_t streamType,
+                                             const DeviceVector& availableDevices);
     // Specify an Output(Sink) route by passing SessionRoute::SOURCE_TYPE_NA in the
     // source argument.
     // Specify an Input(Source) rout by passing SessionRoute::AUDIO_STREAM_DEFAULT
diff --git a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
index 8edd4d1..d34214b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/SessionRoute.cpp
@@ -82,7 +82,7 @@
 void SessionRouteMap::log(const char* caption)
 {
     ALOGI("%s ----", caption);
-    for(size_t index = 0; index < size(); index++) {
+    for (size_t index = 0; index < size(); index++) {
         valueAt(index)->log("  ");
     }
 }
@@ -119,4 +119,22 @@
     }
 }
 
+audio_devices_t SessionRouteMap::getActiveDeviceForStream(audio_stream_type_t streamType,
+                                                          const DeviceVector& availableDevices)
+{
+    audio_devices_t device = AUDIO_DEVICE_NONE;
+
+    for (size_t index = 0; index < size(); index++) {
+        sp<SessionRoute> route = valueAt(index);
+        if (streamType == route->mStreamType && route->isActiveOrChanged()
+                && route->mDeviceDescriptor != 0) {
+            device = route->mDeviceDescriptor->type();
+            if (!availableDevices.getDevicesFromType(device).isEmpty()) {
+                break;
+            }
+        }
+    }
+    return device;
+}
+
 } // namespace android
diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp
index 23c020d..3e13e50 100644
--- a/services/audiopolicy/enginedefault/src/Engine.cpp
+++ b/services/audiopolicy/enginedefault/src/Engine.cpp
@@ -453,6 +453,12 @@
             }
             // Use both Bluetooth SCO and phone default output when ringing in normal mode
             if (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) {
+                if ((strategy == STRATEGY_SONIFICATION) &&
+                        (device & AUDIO_DEVICE_OUT_SPEAKER) &&
+                        (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+                    device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+                    device &= ~AUDIO_DEVICE_OUT_SPEAKER;
+                }
                 if (device2 != AUDIO_DEVICE_NONE) {
                     device |= device2;
                     break;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 1e5f9ae..3775551 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1157,7 +1157,9 @@
         }
     } else if (mOutputRoutes.getAndClearRouteChanged(session)) {
         newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
-        checkStrategyRoute(getStrategy(stream), output);
+        if (newDevice != outputDesc->device()) {
+            checkStrategyRoute(getStrategy(stream), output);
+        }
     } else {
         newDevice = AUDIO_DEVICE_NONE;
     }
@@ -1408,6 +1410,7 @@
                         (newDevice != desc->device())) {
                     audio_devices_t newDevice2 = getNewOutputDevice(desc, false /*fromCache*/);
                     bool force = desc->device() != newDevice2;
+
                     setOutputDevice(desc,
                                     newDevice2,
                                     force,
@@ -4836,6 +4839,20 @@
         }
     }
 
+    // Check if an explicit routing request exists for an active stream on this output and
+    // use it in priority before any other rule
+    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
+        if (outputDesc->isStreamActive((audio_stream_type_t)stream)) {
+            audio_devices_t forcedDevice =
+                    mOutputRoutes.getActiveDeviceForStream(
+                            (audio_stream_type_t)stream, mAvailableOutputDevices);
+
+            if (forcedDevice != AUDIO_DEVICE_NONE) {
+                return forcedDevice;
+            }
+        }
+    }
+
     // check the following by order of priority to request a routing change if necessary:
     // 1: the strategy enforced audible is active and enforced on the output:
     //      use device for strategy enforced audible
@@ -5043,19 +5060,16 @@
 audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                          bool fromCache)
 {
-    // Routing
-    // see if we have an explicit route
-    // scan the whole RouteMap, for each entry, convert the stream type to a strategy
-    // (getStrategy(stream)).
-    // if the strategy from the stream type in the RouteMap is the same as the argument above,
-    // and activity count is non-zero and the device in the route descriptor is available
-    // then select this device.
-    for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
-        sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
-        routing_strategy routeStrategy = getStrategy(route->mStreamType);
-        if ((routeStrategy == strategy) && route->isActiveOrChanged() &&
-                (mAvailableOutputDevices.indexOf(route->mDeviceDescriptor) >= 0)) {
-            return route->mDeviceDescriptor->type();
+    // Check if an explicit routing request exists for a stream type corresponding to the
+    // specified strategy and use it in priority over default routing rules.
+    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
+        if (getStrategy((audio_stream_type_t)stream) == strategy) {
+            audio_devices_t forcedDevice =
+                    mOutputRoutes.getActiveDeviceForStream(
+                            (audio_stream_type_t)stream, mAvailableOutputDevices);
+            if (forcedDevice != AUDIO_DEVICE_NONE) {
+                return forcedDevice;
+            }
         }
     }
 
@@ -5625,7 +5639,10 @@
     }
 
     float volumeDb = computeVolume(stream, index, device);
-    if (outputDesc->isFixedVolume(device)) {
+    if (outputDesc->isFixedVolume(device) ||
+            // Force VoIP volume to max for bluetooth SCO
+            ((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
+             (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
         volumeDb = 0.0f;
     }
 
diff --git a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
index f4d5a18..59ac636 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
+++ b/services/camera/libcameraservice/device3/Camera3StreamSplitter.cpp
@@ -212,7 +212,11 @@
 
     SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
             maxConsumerBuffers, mMaxHalBuffers);
-    size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
+    // The output slot count requirement can change depending on the current amount
+    // of outputs and incoming buffer consumption rate. To avoid any issues with
+    // insufficient slots, set their count to the maximum supported. The output
+    // surface buffer allocation is disabled so no real buffers will get allocated.
+    size_t totalBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
     res = native_window_set_buffer_count(outputQueue.get(),
             totalBufferCount);
     if (res != OK) {