Merge "aaudio: clear data buffer before use" into sc-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index dc22628..a86d2b9 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,7 @@
     name: "com.android.media",
     manifest: "manifest.json",
     defaults: ["com.android.media-defaults"],
+    prebuilts: ["current_sdkinfo"],
 }
 
 linker_config {
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index c77aeeb..946ec17 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -2323,4 +2323,28 @@
     return unexpected(BAD_VALUE);
 }
 
+ConversionResult<TrackSecondaryOutputInfoPair>
+aidl2legacy_TrackSecondaryOutputInfo_TrackSecondaryOutputInfoPair(
+        const media::TrackSecondaryOutputInfo& aidl) {
+    TrackSecondaryOutputInfoPair trackSecondaryOutputInfoPair;
+    trackSecondaryOutputInfoPair.first =
+            VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId));
+    trackSecondaryOutputInfoPair.second =
+            VALUE_OR_RETURN(convertContainer<std::vector<audio_port_handle_t>>(
+                    aidl.secondaryOutputIds, aidl2legacy_int32_t_audio_io_handle_t));
+    return trackSecondaryOutputInfoPair;
+}
+
+ConversionResult<media::TrackSecondaryOutputInfo>
+legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(
+        const TrackSecondaryOutputInfoPair& legacy) {
+    media::TrackSecondaryOutputInfo trackSecondaryOutputInfo;
+    trackSecondaryOutputInfo.portId =
+            VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.first));
+    trackSecondaryOutputInfo.secondaryOutputIds =
+            VALUE_OR_RETURN(convertContainer<std::vector<int32_t>>(
+                    legacy.second, legacy2aidl_audio_io_handle_t_int32_t));
+    return trackSecondaryOutputInfo;
+}
+
 }  // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 19d68a0..43f9660 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -351,6 +351,7 @@
         "aidl/android/media/AudioVibratorInfo.aidl",
         "aidl/android/media/EffectDescriptor.aidl",
         "aidl/android/media/ExtraAudioDescriptor.aidl",
+        "aidl/android/media/TrackSecondaryOutputInfo.aidl",
     ],
     imports: [
         "audio_common-aidl",
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index 389b73f..0564cdf 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -743,6 +743,16 @@
     return statusTFromBinderStatus(mDelegate->setVibratorInfos(vibratorInfos));
 }
 
+status_t AudioFlingerClientAdapter::updateSecondaryOutputs(
+        const TrackSecondaryOutputsMap& trackSecondaryOutputs) {
+    std::vector<media::TrackSecondaryOutputInfo> trackSecondaryOutputInfos =
+            VALUE_OR_RETURN_STATUS(
+                    convertContainer<std::vector<media::TrackSecondaryOutputInfo>>(
+                            trackSecondaryOutputs,
+                            legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo));
+    return statusTFromBinderStatus(mDelegate->updateSecondaryOutputs(trackSecondaryOutputInfos));
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // AudioFlingerServerAdapter
@@ -1199,4 +1209,13 @@
     return Status::fromStatusT(mDelegate->setVibratorInfos(vibratorInfos));
 }
 
+Status AudioFlingerServerAdapter::updateSecondaryOutputs(
+        const std::vector<media::TrackSecondaryOutputInfo>& trackSecondaryOutputInfos) {
+    TrackSecondaryOutputsMap trackSecondaryOutputs =
+            VALUE_OR_RETURN_BINDER(convertContainer<TrackSecondaryOutputsMap>(
+                    trackSecondaryOutputInfos,
+                    aidl2legacy_TrackSecondaryOutputInfo_TrackSecondaryOutputInfoPair));
+    return Status::fromStatusT(mDelegate->updateSecondaryOutputs(trackSecondaryOutputs));
+}
+
 } // namespace android
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index abbced5..d2cae6d 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -40,6 +40,7 @@
 import android.media.IAudioTrack;
 import android.media.MicrophoneInfoData;
 import android.media.RenderPosition;
+import android.media.TrackSecondaryOutputInfo;
 import android.media.audio.common.AudioFormat;
 
 /**
@@ -207,4 +208,9 @@
     // Set vibrators' information.
     // The value will be used to initialize HapticGenerator.
     void setVibratorInfos(in AudioVibratorInfo[] vibratorInfos);
+
+    // Update secondary outputs.
+    // This usually happens when there is a dynamic policy registered.
+    void updateSecondaryOutputs(
+            in TrackSecondaryOutputInfo[] trackSecondaryOutputInfos);
 }
diff --git a/media/libaudioclient/aidl/android/media/TrackSecondaryOutputInfo.aidl b/media/libaudioclient/aidl/android/media/TrackSecondaryOutputInfo.aidl
new file mode 100644
index 0000000..113328e
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/TrackSecondaryOutputInfo.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.media;
+
+/**
+ * This is a class that contains port handle for a track and handles for all secondary
+ * outputs of the track.
+ * @hide
+ */
+parcelable TrackSecondaryOutputInfo {
+    int portId; // audio_port_handle_t
+    int[] secondaryOutputIds; // audio_io_handle_t[]
+}
\ No newline at end of file
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index 1dd9d60..4ec69c7 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -50,6 +50,7 @@
 #include <android/media/AudioUniqueIdUse.h>
 #include <android/media/EffectDescriptor.h>
 #include <android/media/ExtraAudioDescriptor.h>
+#include <android/media/TrackSecondaryOutputInfo.h>
 
 #include <android/media/SharedFileRegion.h>
 #include <binder/IMemory.h>
@@ -407,6 +408,13 @@
 legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(
         const audio_encapsulation_type_t & legacy);
 
+using TrackSecondaryOutputInfoPair = std::pair<audio_port_handle_t, std::vector<audio_io_handle_t>>;
+ConversionResult<TrackSecondaryOutputInfoPair>
+aidl2legacy_TrackSecondaryOutputInfo_TrackSecondaryOutputInfoPair(
+        const media::TrackSecondaryOutputInfo& aidl);
+ConversionResult<media::TrackSecondaryOutputInfo>
+legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(
+        const TrackSecondaryOutputInfoPair& legacy);
 
 
 }  // namespace android
diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h
index 8e446ea..5dfe5fc 100644
--- a/media/libaudioclient/include/media/AudioCommonTypes.h
+++ b/media/libaudioclient/include/media/AudioCommonTypes.h
@@ -29,6 +29,8 @@
 using AttributesVector = std::vector<audio_attributes_t>;
 using StreamTypeVector = std::vector<audio_stream_type_t>;
 
+using TrackSecondaryOutputsMap = std::map<audio_port_handle_t, std::vector<audio_io_handle_t>>;
+
 constexpr bool operator==(const audio_attributes_t &lhs, const audio_attributes_t &rhs)
 {
     return lhs.usage == rhs.usage && lhs.content_type == rhs.content_type &&
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 3a04569..3a4868e 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -26,12 +26,14 @@
 #include <binder/IInterface.h>
 #include <media/AidlConversion.h>
 #include <media/AudioClient.h>
+#include <media/AudioCommonTypes.h>
 #include <media/DeviceDescriptorBase.h>
 #include <system/audio.h>
 #include <system/audio_effect.h>
 #include <system/audio_policy.h>
 #include <utils/String8.h>
 #include <media/MicrophoneInfo.h>
+#include <map>
 #include <string>
 #include <vector>
 
@@ -55,6 +57,7 @@
 #include "android/media/OpenInputResponse.h"
 #include "android/media/OpenOutputRequest.h"
 #include "android/media/OpenOutputResponse.h"
+#include "android/media/TrackSecondaryOutputInfo.h"
 
 namespace android {
 
@@ -338,6 +341,9 @@
     // The values will be used to initialize HapticGenerator.
     virtual status_t setVibratorInfos(
             const std::vector<media::AudioVibratorInfo>& vibratorInfos) = 0;
+
+    virtual status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs) = 0;
 };
 
 /**
@@ -430,6 +436,8 @@
     status_t getMicrophones(std::vector<media::MicrophoneInfo>* microphones) override;
     status_t setAudioHalPids(const std::vector<pid_t>& pids) override;
     status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
+    status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs) override;
 
 private:
     const sp<media::IAudioFlingerService> mDelegate;
@@ -513,6 +521,7 @@
             SET_EFFECT_SUSPENDED = media::BnAudioFlingerService::TRANSACTION_setEffectSuspended,
             SET_AUDIO_HAL_PIDS = media::BnAudioFlingerService::TRANSACTION_setAudioHalPids,
             SET_VIBRATOR_INFOS = media::BnAudioFlingerService::TRANSACTION_setVibratorInfos,
+            UPDATE_SECONDARY_OUTPUTS = media::BnAudioFlingerService::TRANSACTION_updateSecondaryOutputs,
         };
 
         /**
@@ -619,6 +628,8 @@
     Status getMicrophones(std::vector<media::MicrophoneInfoData>* _aidl_return) override;
     Status setAudioHalPids(const std::vector<int32_t>& pids) override;
     Status setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos) override;
+    Status updateSecondaryOutputs(
+            const std::vector<media::TrackSecondaryOutputInfo>& trackSecondaryOutputInfos) override;
 
 private:
     const sp<AudioFlingerServerAdapter::Delegate> mDelegate;
diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp
index e96c041..b26d028 100644
--- a/media/libeffects/downmix/Android.bp
+++ b/media/libeffects/downmix/Android.bp
@@ -18,11 +18,11 @@
     ],
 }
 
-cc_library_shared {
+cc_library {
     name: "libdownmix",
-
+    host_supported: true,
     vendor: true,
-    srcs: ["EffectDownmix.c"],
+    srcs: ["EffectDownmix.cpp"],
 
     shared_libs: [
         "libaudioutils",
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.cpp
similarity index 77%
rename from media/libeffects/downmix/EffectDownmix.c
rename to media/libeffects/downmix/EffectDownmix.cpp
index 5ca5525..f500bc3 100644
--- a/media/libeffects/downmix/EffectDownmix.c
+++ b/media/libeffects/downmix/EffectDownmix.cpp
@@ -16,32 +16,72 @@
 
 #define LOG_TAG "EffectDownmix"
 //#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include <log/log.h>
 
 #include "EffectDownmix.h"
+#include <math.h>
 
 // Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
 //#define DOWNMIX_TEST_CHANNEL_INDEX 0
 // Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
 //#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
 
-#define MINUS_3_DB_IN_FLOAT 0.70710678f // -3dB = 0.70710678f
-const audio_format_t gTargetFormat = AUDIO_FORMAT_PCM_FLOAT;
+#define MINUS_3_DB_IN_FLOAT M_SQRT1_2 // -3dB = 0.70710678
 
-// subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
 typedef enum {
-    CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
-    CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
-    CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
-    CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
-    CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
-} downmix_input_channel_mask_t;
+    DOWNMIX_STATE_UNINITIALIZED,
+    DOWNMIX_STATE_INITIALIZED,
+    DOWNMIX_STATE_ACTIVE,
+} downmix_state_t;
+
+/* parameters for each downmixer */
+typedef struct {
+    downmix_state_t state;
+    downmix_type_t type;
+    bool apply_volume_correction;
+    uint8_t input_channel_count;
+} downmix_object_t;
+
+typedef struct downmix_module_s {
+    const struct effect_interface_s *itfe;
+    effect_config_t config;
+    downmix_object_t context;
+} downmix_module_t;
+
+
+// Audio Effect API
+static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
+        int32_t sessionId,
+        int32_t ioId,
+        effect_handle_t *pHandle);
+static int32_t DownmixLib_Release(effect_handle_t handle);
+static int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
+        effect_descriptor_t *pDescriptor);
+static int32_t Downmix_Process(effect_handle_t self,
+        audio_buffer_t *inBuffer,
+        audio_buffer_t *outBuffer);
+static int32_t Downmix_Command(effect_handle_t self,
+        uint32_t cmdCode,
+        uint32_t cmdSize,
+        void *pCmdData,
+        uint32_t *replySize,
+        void *pReplyData);
+static int32_t Downmix_GetDescriptor(effect_handle_t self,
+        effect_descriptor_t *pDescriptor);
+
+// Internal methods
+static int Downmix_Init(downmix_module_t *pDwmModule);
+static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
+static int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
+static int Downmix_setParameter(
+        downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
+static int Downmix_getParameter(
+        downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
+static void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
+static void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
+static void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate);
+static bool Downmix_foldGeneric(
+        uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate);
 
 // effect_handle_t interface implementation for downmix effect
 const struct effect_interface_s gDownmixInterface = {
@@ -63,7 +103,6 @@
     .get_descriptor = DownmixLib_GetDescriptor,
 };
 
-
 // AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
 static const effect_descriptor_t gDownmixDescriptor = {
         EFFECT_UIID_DOWNMIX__, //type
@@ -84,16 +123,8 @@
 // number of effects in this library
 const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
 
-static LVM_FLOAT clamp_float(LVM_FLOAT a) {
-    if (a > 1.0f) {
-        return 1.0f;
-    }
-    else if (a < -1.0f) {
-        return -1.0f;
-    }
-    else {
-        return a;
-    }
+static inline float clamp_float(float value) {
+    return fmin(fmax(value, -1.f), 1.f);
 }
 
 /*----------------------------------------------------------------------------
@@ -103,7 +134,7 @@
 // strictly for testing, logs the indices of the channels for a given mask,
 // uses the same code as Downmix_foldGeneric()
 void Downmix_testIndexComputation(uint32_t mask) {
-    ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
+    ALOGI("Testing index computation for %#x:", mask);
     // check against unsupported channels
     if (mask & kUnsupported) {
         ALOGE("Unsupported channels (top or front left/right of center)");
@@ -162,29 +193,10 @@
         return false;
     }
     // check against unsupported channels
-    if (mask & kUnsupported) {
-        ALOGE("Unsupported channels (top or front left/right of center)");
+    if (mask & ~AUDIO_CHANNEL_OUT_22POINT2) {
+        ALOGE("Unsupported channels in %u", mask & ~AUDIO_CHANNEL_OUT_22POINT2);
         return false;
     }
-    // verify has FL/FR
-    if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
-        ALOGE("Front channels must be present");
-        return false;
-    }
-    // verify uses SIDE as a pair (ok if not using SIDE at all)
-    if ((mask & kSides) != 0) {
-        if ((mask & kSides) != kSides) {
-            ALOGE("Side channels must be used as a pair");
-            return false;
-        }
-    }
-    // verify uses BACK as a pair (ok if not using BACK at all)
-    if ((mask & kBacks) != 0) {
-        if ((mask & kBacks) != kBacks) {
-            ALOGE("Back channels must be used as a pair");
-            return false;
-        }
-    }
     return true;
 }
 
@@ -194,9 +206,9 @@
 
 /*--- Effect Library Interface Implementation ---*/
 
-int32_t DownmixLib_Create(const effect_uuid_t *uuid,
-        int32_t sessionId __unused,
-        int32_t ioId __unused,
+static int32_t DownmixLib_Create(const effect_uuid_t *uuid,
+        int32_t /* sessionId */,
+        int32_t /* ioId */,
         effect_handle_t *pHandle) {
     int ret;
     int i;
@@ -210,9 +222,9 @@
     ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
                     AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
-    Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
-    Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
-    Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
+    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_QUAD_SIDE | AUDIO_CHANNEL_OUT_QUAD_BACK);
+    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
+    Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
     // shouldn't work (will log an error, won't display channel indices)
     ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
@@ -240,7 +252,7 @@
         return -ENOENT;
     }
 
-    module = malloc(sizeof(downmix_module_t));
+    module = new downmix_module_t{};
 
     module->itfe = &gDownmixInterface;
 
@@ -260,8 +272,7 @@
     return 0;
 }
 
-
-int32_t DownmixLib_Release(effect_handle_t handle) {
+static int32_t DownmixLib_Release(effect_handle_t handle) {
     downmix_module_t *pDwmModule = (downmix_module_t *)handle;
 
     ALOGV("DownmixLib_Release() %p", handle);
@@ -271,12 +282,12 @@
 
     pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
 
-    free(pDwmModule);
+    delete pDwmModule;
     return 0;
 }
 
-
-int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
+static int32_t DownmixLib_GetDescriptor(
+        const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
     ALOGV("DownmixLib_GetDescriptor()");
     int i;
 
@@ -289,7 +300,7 @@
         ALOGV("DownmixLib_GetDescriptor() i=%d", i);
         if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
             memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
-            ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
+            ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %#x",
                  i, gDescriptors[i]->uuid.timeLow);
             return 0;
         }
@@ -300,11 +311,11 @@
 
 /*--- Effect Control Interface Implementation ---*/
 
-static int Downmix_Process(effect_handle_t self,
+static int32_t Downmix_Process(effect_handle_t self,
         audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
 
     downmix_object_t *pDownmixer;
-    LVM_FLOAT *pSrc, *pDst;
+    float *pSrc, *pDst;
     downmix_module_t *pDwmModule = (downmix_module_t *)self;
 
     if (pDwmModule == NULL) {
@@ -327,8 +338,8 @@
         return -ENODATA;
     }
 
-    pSrc = (LVM_FLOAT *) inBuffer->s16;
-    pDst = (LVM_FLOAT *) outBuffer->s16;
+    pSrc = inBuffer->f32;
+    pDst = outBuffer->f32;
     size_t numFrames = outBuffer->frameCount;
 
     const bool accumulate =
@@ -362,29 +373,29 @@
           // bypass the optimized downmix routines for the common formats
           if (!Downmix_foldGeneric(
                   downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
-              ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
+              ALOGE("Multichannel configuration %#x is not supported",
                     downmixInputChannelMask);
               return -EINVAL;
           }
           break;
 #endif
         // optimize for the common formats
-        switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
-        case CHANNEL_MASK_QUAD_BACK:
-        case CHANNEL_MASK_QUAD_SIDE:
+        switch (downmixInputChannelMask) {
+        case AUDIO_CHANNEL_OUT_QUAD_BACK:
+        case AUDIO_CHANNEL_OUT_QUAD_SIDE:
             Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
             break;
-        case CHANNEL_MASK_5POINT1_BACK:
-        case CHANNEL_MASK_5POINT1_SIDE:
+        case AUDIO_CHANNEL_OUT_5POINT1_BACK:
+        case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
             Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
             break;
-        case CHANNEL_MASK_7POINT1:
+        case AUDIO_CHANNEL_OUT_7POINT1:
             Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
             break;
         default:
             if (!Downmix_foldGeneric(
                     downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
-                ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported",
+                ALOGE("Multichannel configuration %#x is not supported",
                       downmixInputChannelMask);
                 return -EINVAL;
             }
@@ -399,7 +410,7 @@
     return 0;
 }
 
-static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
+static int32_t Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
         void *pCmdData, uint32_t *replySize, void *pReplyData) {
 
     downmix_module_t *pDwmModule = (downmix_module_t *) self;
@@ -411,7 +422,7 @@
 
     pDownmixer = (downmix_object_t*) &pDwmModule->context;
 
-    ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
+    ALOGV("Downmix_Command command %u cmdSize %u", cmdCode, cmdSize);
 
     switch (cmdCode) {
     case EFFECT_CMD_INIT:
@@ -434,8 +445,8 @@
         Downmix_Reset(pDownmixer, false);
         break;
 
-    case EFFECT_CMD_GET_PARAM:
-        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
+    case EFFECT_CMD_GET_PARAM: {
+        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %u, pReplyData: %p",
                 pCmdData, *replySize, pReplyData);
         if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
                 pReplyData == NULL || replySize == NULL ||
@@ -444,15 +455,15 @@
         }
         effect_param_t *rep = (effect_param_t *) pReplyData;
         memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
-        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
+        ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %u",
                 *(int32_t *)rep->data, rep->vsize);
         rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
                 rep->data + sizeof(int32_t));
         *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
         break;
-
-    case EFFECT_CMD_SET_PARAM:
-        ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
+    }
+    case EFFECT_CMD_SET_PARAM: {
+        ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %u"
                 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
         if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
                 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
@@ -466,6 +477,7 @@
         *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
                 cmd->vsize, cmd->data + sizeof(int32_t));
         break;
+    }
 
     case EFFECT_CMD_SET_PARAM_DEFERRED:
         //FIXME implement
@@ -506,7 +518,7 @@
             return -EINVAL;
         }
         // FIXME change type if playing on headset vs speaker
-        ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
+        ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: %#x", *(uint32_t *)pCmdData);
         break;
 
     case EFFECT_CMD_SET_VOLUME: {
@@ -526,7 +538,7 @@
         if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
             return -EINVAL;
         }
-        ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
+        ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %u", *(uint32_t *)pCmdData);
         break;
 
     case EFFECT_CMD_SET_CONFIG_REVERSE:
@@ -535,15 +547,14 @@
         break;
 
     default:
-        ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
+        ALOGW("Downmix_Command invalid command %u", cmdCode);
         return -EINVAL;
     }
 
     return 0;
 }
 
-
-int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
+static int32_t Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
 {
     downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
 
@@ -591,7 +602,7 @@
  *----------------------------------------------------------------------------
  */
 
-int Downmix_Init(downmix_module_t *pDwmModule) {
+static int Downmix_Init(downmix_module_t *pDwmModule) {
 
     ALOGV("Downmix_Init module %p", pDwmModule);
     int ret = 0;
@@ -599,7 +610,7 @@
     memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
 
     pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
-    pDwmModule->config.inputCfg.format = gTargetFormat;
+    pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
     pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
     pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
     pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
@@ -611,7 +622,7 @@
 
     // set a default value for the access mode, but should be overwritten by caller
     pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
-    pDwmModule->config.outputCfg.format = gTargetFormat;
+    pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
     pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
     pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
     pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
@@ -651,15 +662,15 @@
  *----------------------------------------------------------------------------
  */
 
-int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
+static int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
 
     downmix_object_t *pDownmixer = &pDwmModule->context;
 
     // Check configuration compatibility with build options, and effect capabilities
     if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
-        || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
-        || pConfig->inputCfg.format != gTargetFormat
-        || pConfig->outputCfg.format != gTargetFormat) {
+        || pConfig->outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
+        || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_FLOAT
+        || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_FLOAT) {
         ALOGE("Downmix_Configure error: invalid config");
         return -EINVAL;
     }
@@ -709,7 +720,7 @@
  *----------------------------------------------------------------------------
  */
 
-int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
+static int Downmix_Reset(downmix_object_t* /* pDownmixer */, bool /* init */) {
     // nothing to do here
     return 0;
 }
@@ -736,31 +747,32 @@
  *
  *----------------------------------------------------------------------------
  */
-int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
+static int Downmix_setParameter(
+        downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
 
     int16_t value16;
-    ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
+    ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
             pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
 
     switch (param) {
 
       case DOWNMIX_PARAM_TYPE:
         if (size != sizeof(downmix_type_t)) {
-            ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
+            ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %u, should be %zu",
                     size, sizeof(downmix_type_t));
             return -EINVAL;
         }
         value16 = *(int16_t *)pValue;
-        ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
+        ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
         if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
-            ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
+            ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
             return -EINVAL;
         } else {
             pDownmixer->type = (downmix_type_t) value16;
         break;
 
       default:
-        ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
+        ALOGE("Downmix_setParameter unknown parameter %d", param);
         return -EINVAL;
     }
 }
@@ -792,24 +804,25 @@
  *
  *----------------------------------------------------------------------------
  */
-int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
+static int Downmix_getParameter(
+        downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
     int16_t *pValue16;
 
     switch (param) {
 
     case DOWNMIX_PARAM_TYPE:
       if (*pSize < sizeof(int16_t)) {
-          ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
+          ALOGE("Downmix_getParameter invalid parameter size %u for DOWNMIX_PARAM_TYPE", *pSize);
           return -EINVAL;
       }
       pValue16 = (int16_t *)pValue;
       *pValue16 = (int16_t) pDownmixer->type;
       *pSize = sizeof(int16_t);
-      ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
+      ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
       break;
 
     default:
-      ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
+      ALOGE("Downmix_getParameter unknown parameter %d", param);
       return -EINVAL;
     }
 
@@ -834,7 +847,7 @@
  *
  *----------------------------------------------------------------------------
  */
-void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
+void Downmix_foldFromQuad(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
     // sample at index 0 is FL
     // sample at index 1 is FR
     // sample at index 2 is RL
@@ -879,8 +892,8 @@
  *
  *----------------------------------------------------------------------------
  */
-void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
-    LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
+void Downmix_foldFrom5Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
+    float lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
     // sample at index 0 is FL
     // sample at index 1 is FR
     // sample at index 2 is FC
@@ -941,8 +954,8 @@
  *
  *----------------------------------------------------------------------------
  */
-void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
-    LVM_FLOAT lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
+void Downmix_foldFrom7Point1(float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
+    float lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
     // sample at index 0 is FL
     // sample at index 1 is FR
     // sample at index 2 is FC
@@ -992,13 +1005,7 @@
  * Downmix_foldGeneric()
  *----------------------------------------------------------------------------
  * Purpose:
- * downmix to stereo a multichannel signal whose format is:
- *  - has FL/FR
- *  - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
- *  - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
- *  - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
- *  - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
- * Only handles channel masks not enumerated in downmix_input_channel_mask_t
+ * downmix to stereo a multichannel signal of arbitrary channel position mask.
  *
  * Inputs:
  *  mask       the channel mask of pSrc
@@ -1015,93 +1022,106 @@
  *----------------------------------------------------------------------------
  */
 bool Downmix_foldGeneric(
-        uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate) {
+        uint32_t mask, float *pSrc, float *pDst, size_t numFrames, bool accumulate) {
 
     if (!Downmix_validChannelMask(mask)) {
         return false;
     }
-
-    const bool hasSides = (mask & kSides) != 0;
-    const bool hasBacks = (mask & kBacks) != 0;
-
     const int numChan = audio_channel_count_from_out_mask(mask);
-    const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
-    const bool hasLFE =
-            ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
-    const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
-    // compute at what index each channel is: samples will be in the following order:
-    //   FL FR FC LFE BL BR BC SL SR
-    // when a channel is not present, its index is set to the same as the index of the preceding
-    // channel
-    const int indexFC  = hasFC    ? 2            : 1;        // front center
-    const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
-    const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
-    const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
-    const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
-    const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
-    const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
 
-    LVM_FLOAT lt, rt, centersLfeContrib;
-    // code is mostly duplicated between the two values of accumulate to avoid repeating the test
-    // for every sample
-    if (accumulate) {
-        while (numFrames) {
-            // compute contribution of FC, BC and LFE
-            centersLfeContrib = 0;
-            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
-            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
-            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
-            centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
-            // always has FL/FR
-            lt = pSrc[0];
-            rt = pSrc[1];
-            // mix in sides and backs
-            if (hasSides) {
-                lt += pSrc[indexSL];
-                rt += pSrc[indexSR];
-            }
-            if (hasBacks) {
-                lt += pSrc[indexBL];
-                rt += pSrc[indexBR];
-            }
-            lt += centersLfeContrib;
-            rt += centersLfeContrib;
-            // accumulate in destination
-            pDst[0] = clamp_float(pDst[0] + (lt / 2.0f));
-            pDst[1] = clamp_float(pDst[1] + (rt / 2.0f));
-            pSrc += numChan;
-            pDst += 2;
-            numFrames--;
+    // compute at what index each channel is: samples will be in the following order:
+    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
+    //
+    //  (transfer matrix)
+    //   FL  FR  FC    LFE   BL  BR  BC    SL  SR
+    //   0.5     0.353 0.353 0.5     0.353 0.5
+    //       0.5 0.353 0.353     0.5 0.353     0.5
+
+    // derive the indices for the transfer matrix columns that have non-zero values.
+    int indexFL = -1;
+    int indexFR = -1;
+    int indexFC = -1;
+    int indexLFE = -1;
+    int indexBL = -1;
+    int indexBR = -1;
+    int indexBC = -1;
+    int indexSL = -1;
+    int indexSR = -1;
+    int index = 0;
+    for (unsigned tmp = mask;
+         (tmp & (AUDIO_CHANNEL_OUT_7POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER)) != 0;
+         ++index) {
+        const unsigned lowestBit = tmp & -(signed)tmp;
+        switch (lowestBit) {
+        case AUDIO_CHANNEL_OUT_FRONT_LEFT:
+            indexFL = index;
+            break;
+        case AUDIO_CHANNEL_OUT_FRONT_RIGHT:
+            indexFR = index;
+            break;
+        case AUDIO_CHANNEL_OUT_FRONT_CENTER:
+            indexFC = index;
+            break;
+        case AUDIO_CHANNEL_OUT_LOW_FREQUENCY:
+            indexLFE = index;
+            break;
+        case AUDIO_CHANNEL_OUT_BACK_LEFT:
+            indexBL = index;
+            break;
+        case AUDIO_CHANNEL_OUT_BACK_RIGHT:
+            indexBR = index;
+            break;
+        case AUDIO_CHANNEL_OUT_BACK_CENTER:
+            indexBC = index;
+            break;
+        case AUDIO_CHANNEL_OUT_SIDE_LEFT:
+            indexSL = index;
+            break;
+        case AUDIO_CHANNEL_OUT_SIDE_RIGHT:
+            indexSR = index;
+            break;
         }
-    } else {
-        while (numFrames) {
-            // compute contribution of FC, BC and LFE
-            centersLfeContrib = 0;
-            if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
-            if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
-            if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
-            centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
-            // always has FL/FR
-            lt = pSrc[0];
-            rt = pSrc[1];
-            // mix in sides and backs
-            if (hasSides) {
-                lt += pSrc[indexSL];
-                rt += pSrc[indexSR];
-            }
-            if (hasBacks) {
-                lt += pSrc[indexBL];
-                rt += pSrc[indexBR];
-            }
-            lt += centersLfeContrib;
-            rt += centersLfeContrib;
-            // store in destination
-            pDst[0] = clamp_float(lt / 2.0f); // differs from when accumulate is true above
-            pDst[1] = clamp_float(rt / 2.0f); // differs from when accumulate is true above
-            pSrc += numChan;
-            pDst += 2;
-            numFrames--;
+        tmp ^= lowestBit;
+    }
+
+    // With good branch prediction, this should run reasonably fast.
+    // Also consider using a transfer matrix form.
+    while (numFrames) {
+        // compute contribution of FC, BC and LFE
+        float centersLfeContrib = 0;
+        if (indexFC >= 0) centersLfeContrib = pSrc[indexFC];
+        if (indexLFE >= 0) centersLfeContrib += pSrc[indexLFE];
+        if (indexBC >= 0) centersLfeContrib += pSrc[indexBC];
+        centersLfeContrib *= MINUS_3_DB_IN_FLOAT;
+
+        float ch[2];
+        ch[0] = centersLfeContrib;
+        ch[1] = centersLfeContrib;
+
+        // mix in left / right channels
+        if (indexFL >= 0) ch[0] += pSrc[indexFL];
+        if (indexFR >= 0) ch[1] += pSrc[indexFR];
+
+        if (indexSL >= 0) ch[0] += pSrc[indexSL];
+        if (indexSR >= 0) ch[1] += pSrc[indexSR]; // note pair checks enforce this if indexSL != 0
+
+        if (indexBL >= 0) ch[0] += pSrc[indexBL];
+        if (indexBR >= 0) ch[1] += pSrc[indexBR]; // note pair checks enforce this if indexBL != 0
+
+        // scale to prevent overflow.
+        ch[0] *= 0.5f;
+        ch[1] *= 0.5f;
+
+        if (accumulate) {
+            ch[0] += pDst[0];
+            ch[1] += pDst[1];
         }
+
+        pDst[0] = clamp_float(ch[0]);
+        pDst[1] = clamp_float(ch[1]);
+        pSrc += numChan;
+        pDst += 2;
+        numFrames--;
     }
     return true;
 }
diff --git a/media/libeffects/downmix/EffectDownmix.h b/media/libeffects/downmix/EffectDownmix.h
index 679a855..1206520 100644
--- a/media/libeffects/downmix/EffectDownmix.h
+++ b/media/libeffects/downmix/EffectDownmix.h
@@ -18,88 +18,9 @@
 #define ANDROID_EFFECTDOWNMIX_H_
 
 #include <audio_effects/effect_downmix.h>
-#include <audio_utils/primitives.h>
 #include <system/audio.h>
 
-/*------------------------------------
- * definitions
- *------------------------------------
-*/
-
-#define DOWNMIX_OUTPUT_CHANNELS AUDIO_CHANNEL_OUT_STEREO
-#define LVM_FLOAT float
-
-typedef enum {
-    DOWNMIX_STATE_UNINITIALIZED,
-    DOWNMIX_STATE_INITIALIZED,
-    DOWNMIX_STATE_ACTIVE,
-} downmix_state_t;
-
-/* parameters for each downmixer */
-typedef struct {
-    downmix_state_t state;
-    downmix_type_t type;
-    bool apply_volume_correction;
-    uint8_t input_channel_count;
-} downmix_object_t;
-
-
-typedef struct downmix_module_s {
-    const struct effect_interface_s *itfe;
-    effect_config_t config;
-    downmix_object_t context;
-} downmix_module_t;
-
-const uint32_t kSides = AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT;
-const uint32_t kBacks = AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT;
-const uint32_t kUnsupported =
-        AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
-        AUDIO_CHANNEL_OUT_TOP_CENTER |
-        AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT |
-        AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER |
-        AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT |
-        AUDIO_CHANNEL_OUT_TOP_BACK_LEFT |
-        AUDIO_CHANNEL_OUT_TOP_BACK_CENTER |
-        AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT;
-
-/*------------------------------------
- * Effect API
- *------------------------------------
-*/
-int32_t DownmixLib_Create(const effect_uuid_t *uuid,
-        int32_t sessionId,
-        int32_t ioId,
-        effect_handle_t *pHandle);
-int32_t DownmixLib_Release(effect_handle_t handle);
-int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
-        effect_descriptor_t *pDescriptor);
-
-static int Downmix_Process(effect_handle_t self,
-        audio_buffer_t *inBuffer,
-        audio_buffer_t *outBuffer);
-static int Downmix_Command(effect_handle_t self,
-        uint32_t cmdCode,
-        uint32_t cmdSize,
-        void *pCmdData,
-        uint32_t *replySize,
-        void *pReplyData);
-static int Downmix_GetDescriptor(effect_handle_t self,
-        effect_descriptor_t *pDescriptor);
-
-
-/*------------------------------------
- * internal functions
- *------------------------------------
-*/
-int Downmix_Init(downmix_module_t *pDwmModule);
-int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
-int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
-int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue);
-int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue);
-void Downmix_foldFromQuad(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate);
-void Downmix_foldFrom5Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate);
-void Downmix_foldFrom7Point1(LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate);
-bool Downmix_foldGeneric(
-        uint32_t mask, LVM_FLOAT *pSrc, LVM_FLOAT *pDst, size_t numFrames, bool accumulate);
+// Use the following declaration to obtain the Downmix library information.
+// extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
 
 #endif /*ANDROID_EFFECTDOWNMIX_H_*/
diff --git a/media/libeffects/downmix/benchmark/Android.bp b/media/libeffects/downmix/benchmark/Android.bp
new file mode 100644
index 0000000..10f14e2
--- /dev/null
+++ b/media/libeffects/downmix/benchmark/Android.bp
@@ -0,0 +1,38 @@
+// Build testbench for downmix module.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_av_media_libeffects_downmix_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: [
+        "frameworks_av_media_libeffects_downmix_license",
+    ],
+}
+
+cc_benchmark {
+    name: "downmix_benchmark",
+    host_supported: false,
+    vendor: true,
+    include_dirs: [
+        "frameworks/av/media/libeffects/downmix",
+    ],
+    header_libs: [
+        "libaudioeffects",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libaudioutils",
+        "libdownmix",
+    ],
+    srcs: [
+        "downmix_benchmark.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
diff --git a/media/libeffects/downmix/benchmark/downmix_benchmark.cpp b/media/libeffects/downmix/benchmark/downmix_benchmark.cpp
new file mode 100644
index 0000000..ee169c2
--- /dev/null
+++ b/media/libeffects/downmix/benchmark/downmix_benchmark.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2021 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 <random>
+#include <vector>
+
+#include <audio_effects/effect_downmix.h>
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/Statistics.h>
+#include <benchmark/benchmark.h>
+#include <log/log.h>
+#include <system/audio.h>
+
+#include "EffectDownmix.h"
+
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+
+static constexpr audio_channel_mask_t kChannelPositionMasks[] = {
+    AUDIO_CHANNEL_OUT_FRONT_LEFT,
+    AUDIO_CHANNEL_OUT_FRONT_CENTER,
+    AUDIO_CHANNEL_OUT_STEREO,
+    AUDIO_CHANNEL_OUT_2POINT1,
+    AUDIO_CHANNEL_OUT_2POINT0POINT2,
+    AUDIO_CHANNEL_OUT_QUAD,
+    AUDIO_CHANNEL_OUT_QUAD_BACK,
+    AUDIO_CHANNEL_OUT_QUAD_SIDE,
+    AUDIO_CHANNEL_OUT_SURROUND,
+    AUDIO_CHANNEL_OUT_2POINT1POINT2,
+    AUDIO_CHANNEL_OUT_3POINT0POINT2,
+    AUDIO_CHANNEL_OUT_PENTA,
+    AUDIO_CHANNEL_OUT_3POINT1POINT2,
+    AUDIO_CHANNEL_OUT_5POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1_BACK,
+    AUDIO_CHANNEL_OUT_5POINT1_SIDE,
+    AUDIO_CHANNEL_OUT_6POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1POINT2,
+    AUDIO_CHANNEL_OUT_7POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1POINT4,
+    AUDIO_CHANNEL_OUT_7POINT1POINT2,
+    AUDIO_CHANNEL_OUT_7POINT1POINT4,
+    AUDIO_CHANNEL_OUT_13POINT_360RA,
+    AUDIO_CHANNEL_OUT_22POINT2,
+};
+
+static constexpr effect_uuid_t downmix_uuid = {
+    0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
+
+static constexpr size_t kFrameCount = 1000;
+
+/*
+Pixel 3XL
+downmix_benchmark:
+  #BM_Downmix/0     4723 ns    4708 ns       148694
+  #BM_Downmix/1     4717 ns    4702 ns       148873
+  #BM_Downmix/2     4803 ns    4788 ns       145893
+  #BM_Downmix/3     5056 ns    5041 ns       139110
+  #BM_Downmix/4     4710 ns    4696 ns       149625
+  #BM_Downmix/5     1514 ns    1509 ns       463694
+  #BM_Downmix/6     1513 ns    1509 ns       463451
+  #BM_Downmix/7     1516 ns    1511 ns       463899
+  #BM_Downmix/8     4445 ns    4431 ns       157831
+  #BM_Downmix/9     5081 ns    5065 ns       138412
+  #BM_Downmix/10    4354 ns    4341 ns       161247
+  #BM_Downmix/11    4411 ns    4397 ns       158893
+  #BM_Downmix/12    4434 ns    4420 ns       157992
+  #BM_Downmix/13    4845 ns    4830 ns       144873
+  #BM_Downmix/14    4851 ns    4835 ns       144954
+  #BM_Downmix/15    4884 ns    4870 ns       144233
+  #BM_Downmix/16    5832 ns    5813 ns       120565
+  #BM_Downmix/17    5241 ns    5224 ns       133927
+  #BM_Downmix/18    5044 ns    5028 ns       139131
+  #BM_Downmix/19    5244 ns    5227 ns       132315
+  #BM_Downmix/20    5943 ns    5923 ns       117759
+  #BM_Downmix/21    5990 ns    5971 ns       117263
+  #BM_Downmix/22    4468 ns    4454 ns       156689
+  #BM_Downmix/23    7306 ns    7286 ns        95911
+--
+downmix_benchmark: (generic fold)
+  #BM_Downmix/0     4722 ns    4707 ns       149847
+  #BM_Downmix/1     4714 ns    4698 ns       148748
+  #BM_Downmix/2     4794 ns    4779 ns       145661
+  #BM_Downmix/3     5053 ns    5035 ns       139172
+  #BM_Downmix/4     4695 ns    4678 ns       149762
+  #BM_Downmix/5     4381 ns    4368 ns       159675
+  #BM_Downmix/6     4387 ns    4373 ns       160267
+  #BM_Downmix/7     4732 ns    4717 ns       148514
+  #BM_Downmix/8     4430 ns    4415 ns       158133
+  #BM_Downmix/9     5101 ns    5084 ns       138353
+  #BM_Downmix/10    4356 ns    4343 ns       160821
+  #BM_Downmix/11    4397 ns    4383 ns       159995
+  #BM_Downmix/12    4438 ns    4424 ns       158117
+  #BM_Downmix/13    5243 ns    5226 ns       133863
+  #BM_Downmix/14    5259 ns    5242 ns       131855
+  #BM_Downmix/15    5245 ns    5228 ns       133686
+  #BM_Downmix/16    5829 ns    5809 ns       120543
+  #BM_Downmix/17    5245 ns    5228 ns       133533
+  #BM_Downmix/18    5935 ns    5916 ns       118282
+  #BM_Downmix/19    5263 ns    5245 ns       133657
+  #BM_Downmix/20    5998 ns    5978 ns       114693
+  #BM_Downmix/21    5989 ns    5969 ns       117450
+  #BM_Downmix/22    4442 ns    4431 ns       157913
+  #BM_Downmix/23    7309 ns    7290 ns        95797
+*/
+
+static void BM_Downmix(benchmark::State& state) {
+    const audio_channel_mask_t channelMask = kChannelPositionMasks[state.range(0)];
+    const size_t channelCount = audio_channel_count_from_out_mask(channelMask);
+    const int sampleRate = 48000;
+
+    // Initialize input buffer with deterministic pseudo-random values
+    std::minstd_rand gen(channelMask);
+    std::uniform_real_distribution<> dis(-1.0f, 1.0f);
+    std::vector<float> input(kFrameCount * channelCount);
+    std::vector<float> output(kFrameCount * 2);
+    for (auto& in : input) {
+        in = dis(gen);
+    }
+    effect_handle_t effectHandle = nullptr;
+    if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
+            &downmix_uuid, 1, 1, &effectHandle);
+        status != 0) {
+        ALOGE("create_effect returned an error = %d\n", status);
+        return;
+    }
+
+    effect_config_t config{};
+    config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+    config.inputCfg.bufferProvider.getBuffer = nullptr;
+    config.inputCfg.bufferProvider.releaseBuffer = nullptr;
+    config.inputCfg.bufferProvider.cookie = nullptr;
+    config.inputCfg.mask = EFFECT_CONFIG_ALL;
+
+    config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+    config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+    config.outputCfg.bufferProvider.getBuffer = nullptr;
+    config.outputCfg.bufferProvider.releaseBuffer = nullptr;
+    config.outputCfg.bufferProvider.cookie = nullptr;
+    config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    config.inputCfg.samplingRate = sampleRate;
+    config.inputCfg.channels = channelMask;
+
+    config.outputCfg.samplingRate = sampleRate;
+    config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; // output always stereo
+
+    int reply = 0;
+    uint32_t replySize = sizeof(reply);
+    if (int status = (*effectHandle)
+            ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
+                    &config, &replySize, &reply);
+        status != 0) {
+        ALOGE("command returned an error = %d\n", status);
+        return;
+    }
+
+    if (int status = (*effectHandle)
+            ->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
+        status != 0) {
+        ALOGE("Command enable call returned error %d\n", reply);
+        return;
+    }
+
+    // Run the test
+    for (auto _ : state) {
+        benchmark::DoNotOptimize(input.data());
+        benchmark::DoNotOptimize(output.data());
+
+        audio_buffer_t inBuffer = {.frameCount = kFrameCount, .f32 = input.data()};
+        audio_buffer_t outBuffer = {.frameCount = kFrameCount, .f32 = output.data()};
+        (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
+
+        benchmark::ClobberMemory();
+    }
+
+    state.SetComplexityN(state.range(0));
+
+    if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
+        ALOGE("release_effect returned an error = %d\n", status);
+        return;
+    }
+}
+
+static void DownmixArgs(benchmark::internal::Benchmark* b) {
+    for (int i = 0; i < (int)std::size(kChannelPositionMasks); i++) {
+        b->Args({i});
+    }
+}
+
+BENCHMARK(BM_Downmix)->Apply(DownmixArgs);
+
+BENCHMARK_MAIN();
diff --git a/media/libeffects/downmix/tests/Android.bp b/media/libeffects/downmix/tests/Android.bp
index 4077312..4940117 100644
--- a/media/libeffects/downmix/tests/Android.bp
+++ b/media/libeffects/downmix/tests/Android.bp
@@ -10,6 +10,43 @@
     ],
 }
 
+// This is a gtest unit test.
+//
+// Use "atest downmix_tests" to run.
+cc_test {
+    name:"downmix_tests",
+    gtest: true,
+    host_supported: true,
+    vendor: true,
+    include_dirs: [
+        "frameworks/av/media/libeffects/downmix",
+    ],
+    header_libs: [
+        "libaudioeffects",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+    static_libs: [
+        "libaudioutils",
+        "libdownmix",
+    ],
+    srcs: [
+        "downmix_tests.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
+
+// This is a test application which generates downmixed files for regression
+// analysis.
+//
+// See build_and_run_all_unit_tests.sh for a test script that uses this
+// test application and outputs then compares files in a local directory
+// on device (/data/local/tmp/downmixtest/).
 cc_test {
     name:"downmixtest",
     host_supported: false,
diff --git a/media/libeffects/downmix/tests/downmix_tests.cpp b/media/libeffects/downmix/tests/downmix_tests.cpp
new file mode 100644
index 0000000..d4b7a3a
--- /dev/null
+++ b/media/libeffects/downmix/tests/downmix_tests.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2021 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 <vector>
+
+#include "EffectDownmix.h"
+
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
+#include <audio_utils/Statistics.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
+static constexpr audio_channel_mask_t kChannelPositionMasks[] = {
+    AUDIO_CHANNEL_OUT_FRONT_LEFT, // Legacy: the downmix effect treats MONO as FRONT_LEFT only.
+                                  // The AudioMixer interprets MONO as a special case requiring
+                                  // channel replication, bypassing the downmix effect.
+    AUDIO_CHANNEL_OUT_FRONT_CENTER,
+    AUDIO_CHANNEL_OUT_STEREO,
+    AUDIO_CHANNEL_OUT_2POINT1,
+    AUDIO_CHANNEL_OUT_2POINT0POINT2,
+    AUDIO_CHANNEL_OUT_QUAD,
+    AUDIO_CHANNEL_OUT_QUAD_BACK,
+    AUDIO_CHANNEL_OUT_QUAD_SIDE,
+    AUDIO_CHANNEL_OUT_SURROUND,
+    AUDIO_CHANNEL_OUT_2POINT1POINT2,
+    AUDIO_CHANNEL_OUT_3POINT0POINT2,
+    AUDIO_CHANNEL_OUT_PENTA,
+    AUDIO_CHANNEL_OUT_3POINT1POINT2,
+    AUDIO_CHANNEL_OUT_5POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1_BACK,
+    AUDIO_CHANNEL_OUT_5POINT1_SIDE,
+    AUDIO_CHANNEL_OUT_6POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1POINT2,
+    AUDIO_CHANNEL_OUT_7POINT1,
+    AUDIO_CHANNEL_OUT_5POINT1POINT4,
+    AUDIO_CHANNEL_OUT_7POINT1POINT2,
+    AUDIO_CHANNEL_OUT_7POINT1POINT4,
+    AUDIO_CHANNEL_OUT_13POINT_360RA,
+    AUDIO_CHANNEL_OUT_22POINT2,
+};
+
+static constexpr audio_channel_mask_t kConsideredChannels =
+    (audio_channel_mask_t)(AUDIO_CHANNEL_OUT_7POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER);
+
+// Downmix doesn't change with sample rate
+static constexpr size_t kSampleRates[] = {
+    48000,
+};
+
+// Our near expectation is 16x the bit that doesn't fit the mantissa.
+// this works so long as we add values close in exponent with each other
+// realizing that errors accumulate as the sqrt of N (random walk, lln, etc).
+#define EXPECT_NEAR_EPSILON(e, v) EXPECT_NEAR((e), (v), \
+        abs((e) * std::numeric_limits<std::decay_t<decltype(e)>>::epsilon() * 8))
+
+template<typename T>
+static auto channelStatistics(const std::vector<T>& input, size_t channels) {
+    std::vector<android::audio_utils::Statistics<T>> result(channels);
+    const size_t frames = input.size() / channels;
+    if (frames > 0) {
+        const float *fptr = input.data();
+        for (size_t i = 0; i < frames; ++i) {
+            for (size_t j = 0; j < channels; ++j) {
+                result[j].add(*fptr++);
+            }
+        }
+    }
+    return result;
+}
+
+using DownmixParam = std::tuple<int /* sample rate */,  int /* channel mask */>;
+class DownmixTest : public ::testing::TestWithParam<DownmixParam> {
+public:
+    static constexpr effect_uuid_t downmix_uuid_ = {
+        0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
+    static constexpr size_t FRAME_LENGTH = 256;
+
+    void testBalance(int sampleRate, audio_channel_mask_t channelMask) {
+        using namespace ::android::audio_utils::channels;
+
+        size_t frames = 100;
+        unsigned outChannels = 2;
+        unsigned inChannels = audio_channel_count_from_out_mask(channelMask);
+        std::vector<float> input(frames * inChannels);
+        std::vector<float> output(frames * outChannels);
+
+        double savedPower[32][2]{};
+        for (unsigned i = 0, channel = channelMask; channel != 0; ++i) {
+            const int index = __builtin_ctz(channel);
+            ASSERT_LT(index, FCC_24);
+            const int pairIndex = pairIdxFromChannelIdx(index);
+            const AUDIO_GEOMETRY_SIDE side = sideFromChannelIdx(index);
+            const int channelBit = 1 << index;
+            channel &= ~channelBit;
+
+            // Generate a +1, -1 alternating stream in one channel, which has variance 1.
+            auto indata = input.data();
+            for (unsigned j = 0; j < frames; ++j) {
+                for (unsigned k = 0; k < inChannels; ++k) {
+                    *indata++ = (k == i) ? (j & 1 ? -1 : 1) : 0;
+                }
+            }
+            run(sampleRate, channelMask, input, output, frames);
+
+            auto stats = channelStatistics(output, 2 /* channels */);
+            // printf("power: %s %s\n", stats[0].toString().c_str(), stats[1].toString().c_str());
+            double power[2] = { stats[0].getVariance(), stats[1].getVariance() };
+
+            // Check symmetric power for pair channels on exchange of left/right position.
+            // to do this, we save previous power measurements.
+            if (pairIndex >= 0 && pairIndex < index) {
+                EXPECT_NEAR_EPSILON(power[0], savedPower[pairIndex][1]);
+                EXPECT_NEAR_EPSILON(power[1], savedPower[pairIndex][0]);
+            }
+            savedPower[index][0] = power[0];
+            savedPower[index][1] = power[1];
+
+            // Confirm exactly the mix amount prescribed by the existing downmix effect.
+            // For future changes to the downmix effect, the nearness needs to be relaxed
+            // to compare behavior S or earlier.
+            if ((channelBit & kConsideredChannels) == 0) {
+                // for channels not considered, expect 0 power for legacy downmix
+                EXPECT_EQ(0.f, power[0]);
+                EXPECT_EQ(0.f, power[1]);
+                continue;
+            }
+            constexpr float POWER_TOLERANCE = 0.01;  // for variance sum error.
+            switch (side) {
+            case AUDIO_GEOMETRY_SIDE_LEFT:
+                EXPECT_NEAR(0.25f, power[0], POWER_TOLERANCE);
+                EXPECT_EQ(0.f, power[1]);
+                break;
+            case AUDIO_GEOMETRY_SIDE_RIGHT:
+                EXPECT_EQ(0.f, power[0]);
+                EXPECT_NEAR(0.25f, power[1], POWER_TOLERANCE);
+                break;
+            case AUDIO_GEOMETRY_SIDE_CENTER:
+                EXPECT_NEAR(0.125f, power[0], POWER_TOLERANCE);
+                EXPECT_NEAR(0.125f, power[1], POWER_TOLERANCE);
+                EXPECT_NEAR_EPSILON(power[0], power[1]);
+                break;
+            }
+        }
+    }
+
+    void run(int sampleRate, audio_channel_mask_t channelMask,
+            std::vector<float>& input, std::vector<float>& output, size_t frames) {
+        reconfig(sampleRate, channelMask);
+
+        ASSERT_EQ(frames * inputChannelCount_, input.size());
+        ASSERT_EQ(frames * outputChannelCount_, output.size());
+
+        const int32_t sessionId = 0;
+        const int32_t ioId = 0;
+        int32_t err = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
+                &downmix_uuid_, sessionId, ioId,  &handle_);
+        ASSERT_EQ(0, err);
+
+        const struct effect_interface_s * const downmixApi = *handle_;
+        int32_t reply = 0;
+        uint32_t replySize = (uint32_t)sizeof(reply);
+        err = (downmixApi->command)(
+                handle_, EFFECT_CMD_SET_CONFIG,
+                sizeof(effect_config_t), &config_, &replySize, &reply);
+        ASSERT_EQ(0, err);
+        err = (downmixApi->command)(
+                handle_, EFFECT_CMD_ENABLE,
+                0, nullptr, &replySize, &reply);
+        ASSERT_EQ(0, err);
+
+        process(input, output, frames);
+        err = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(handle_);
+        ASSERT_EQ(0, err);
+    }
+
+private:
+    void reconfig(int sampleRate, audio_channel_mask_t channelMask) {
+        config_.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+        config_.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+        config_.inputCfg.bufferProvider.getBuffer = nullptr;
+        config_.inputCfg.bufferProvider.releaseBuffer = nullptr;
+        config_.inputCfg.bufferProvider.cookie = nullptr;
+        config_.inputCfg.mask = EFFECT_CONFIG_ALL;
+
+        config_.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
+        config_.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
+        config_.outputCfg.bufferProvider.getBuffer = nullptr;
+        config_.outputCfg.bufferProvider.releaseBuffer = nullptr;
+        config_.outputCfg.bufferProvider.cookie = nullptr;
+        config_.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+        config_.inputCfg.samplingRate = sampleRate;
+        config_.inputCfg.channels = channelMask;
+        inputChannelCount_ = audio_channel_count_from_out_mask(config_.inputCfg.channels);
+
+        config_.outputCfg.samplingRate = sampleRate;
+        config_.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; // output always stereo
+        outputChannelCount_ = audio_channel_count_from_out_mask(config_.outputCfg.channels);
+    }
+
+    void process(std::vector<float> &input, std::vector<float> &output, size_t frames) const {
+        const struct effect_interface_s * const downmixApi = *handle_;
+
+        for (size_t pos = 0; pos < frames;) {
+            const size_t transfer = std::min(frames - pos, FRAME_LENGTH);
+            audio_buffer_t inbuffer{.frameCount = transfer,
+                .f32 = input.data() + pos * inputChannelCount_};
+            audio_buffer_t outbuffer{.frameCount = transfer,
+                .f32 = output.data() + pos * outputChannelCount_};
+            const int32_t err = (downmixApi->process)(handle_, &inbuffer, &outbuffer);
+            ASSERT_EQ(0, err);
+            pos += transfer;
+        }
+    }
+
+    effect_handle_t handle_{};
+    effect_config_t config_{};
+    int outputChannelCount_{};
+    int inputChannelCount_{};
+};
+
+TEST_P(DownmixTest, basic) {
+    testBalance(kSampleRates[std::get<0>(GetParam())],
+            kChannelPositionMasks[std::get<1>(GetParam())]);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        DownmixTestAll, DownmixTest,
+        ::testing::Combine(
+                ::testing::Range(0, (int)std::size(kSampleRates)),
+                ::testing::Range(0, (int)std::size(kChannelPositionMasks))
+                ));
+
+int main(int argc, /* const */ char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    return status;
+}
diff --git a/media/libeffects/downmix/tests/downmixtest.cpp b/media/libeffects/downmix/tests/downmixtest.cpp
index 71f83e5..4076320 100644
--- a/media/libeffects/downmix/tests/downmixtest.cpp
+++ b/media/libeffects/downmix/tests/downmixtest.cpp
@@ -29,7 +29,6 @@
 #define MAX_NUM_CHANNELS 8
 
 struct downmix_cntxt_s {
-  effect_descriptor_t desc;
   effect_handle_t handle;
   effect_config_t config;
 
@@ -87,13 +86,12 @@
 }
 
 int32_t DownmixConfiureAndEnable(downmix_cntxt_s *pDescriptor) {
-  effect_handle_t *effectHandle = &pDescriptor->handle;
-  downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle;
-  const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe;
+  effect_handle_t effectHandle = pDescriptor->handle;
+  const struct effect_interface_s *Downmix_api = *effectHandle;
   int32_t err = 0;
   uint32_t replySize = (uint32_t)sizeof(err);
 
-  err = (Downmix_api->command)(*effectHandle, EFFECT_CMD_SET_CONFIG,
+  err = (Downmix_api->command)(effectHandle, EFFECT_CMD_SET_CONFIG,
                                sizeof(effect_config_t), &(pDescriptor->config),
                                &replySize, &err);
   if (err != 0) {
@@ -101,7 +99,7 @@
     return err;
   }
 
-  err = ((Downmix_api->command))(*effectHandle, EFFECT_CMD_ENABLE, 0, nullptr,
+  err = ((Downmix_api->command))(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr,
                                  &replySize, &err);
   if (err != 0) {
     ALOGE("Downmix command to enable effect returned an error %d", err);
@@ -112,9 +110,8 @@
 
 int32_t DownmixExecute(downmix_cntxt_s *pDescriptor, FILE *finp,
                        FILE *fout) {
-  effect_handle_t *effectHandle = &pDescriptor->handle;
-  downmix_module_t *downmixEffectHandle = (downmix_module_t *)*effectHandle;
-  const struct effect_interface_s *Downmix_api = downmixEffectHandle->itfe;
+  effect_handle_t effectHandle = pDescriptor->handle;
+  const struct effect_interface_s *Downmix_api = *effectHandle;
 
   const int numFileChannels = pDescriptor->numFileChannels;
   const int numProcessChannels = pDescriptor->numProcessChannels;
@@ -150,7 +147,7 @@
     memcpy_to_float_from_i16(inFloat.data(), inS16.data(),
                              FRAME_LENGTH * numProcessChannels);
 
-    const int32_t err = (Downmix_api->process)(*effectHandle, pinbuf, poutbuf);
+    const int32_t err = (Downmix_api->process)(effectHandle, pinbuf, poutbuf);
     if (err != 0) {
       ALOGE("DownmixProcess returned an error %d", err);
       return -1;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 3562b00..00f423c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -312,6 +312,27 @@
     return NO_ERROR;
 }
 
+status_t AudioFlinger::updateSecondaryOutputs(
+        const TrackSecondaryOutputsMap& trackSecondaryOutputs) {
+    Mutex::Autolock _l(mLock);
+    for (const auto& [trackId, secondaryOutputs] : trackSecondaryOutputs) {
+        size_t i = 0;
+        for (; i < mPlaybackThreads.size(); ++i) {
+            PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+            Mutex::Autolock _tl(thread->mLock);
+            sp<PlaybackThread::Track> track = thread->getTrackById_l(trackId);
+            if (track != nullptr) {
+                ALOGD("%s trackId: %u", __func__, trackId);
+                updateSecondaryOutputsForTrack_l(track.get(), thread, secondaryOutputs);
+                break;
+            }
+        }
+        ALOGW_IF(i >= mPlaybackThreads.size(),
+                 "%s cannot find track with id %u", __func__, trackId);
+    }
+    return NO_ERROR;
+}
+
 // getDefaultVibratorInfo_l must be called with AudioFlinger lock held.
 const media::AudioVibratorInfo* AudioFlinger::getDefaultVibratorInfo_l() {
     if (mAudioVibratorInfos.empty()) {
@@ -944,88 +965,7 @@
             // Connect secondary outputs. Failure on a secondary output must not imped the primary
             // Any secondary output setup failure will lead to a desync between the AP and AF until
             // the track is destroyed.
-            TeePatches teePatches;
-            for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
-                PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
-                if (secondaryThread == NULL) {
-                    ALOGE("no playback thread found for secondary output %d", output.outputId);
-                    continue;
-                }
-
-                size_t sourceFrameCount = thread->frameCount() * output.sampleRate
-                                          / thread->sampleRate();
-                size_t sinkFrameCount = secondaryThread->frameCount() * output.sampleRate
-                                          / secondaryThread->sampleRate();
-                // If the secondary output has just been opened, the first secondaryThread write
-                // will not block as it will fill the empty startup buffer of the HAL,
-                // so a second sink buffer needs to be ready for the immediate next blocking write.
-                // Additionally, have a margin of one main thread buffer as the scheduling jitter
-                // can reorder the writes (eg if thread A&B have the same write intervale,
-                // the scheduler could schedule AB...BA)
-                size_t frameCountToBeReady = 2 * sinkFrameCount + sourceFrameCount;
-                // Total secondary output buffer must be at least as the read frames plus
-                // the margin of a few buffers on both sides in case the
-                // threads scheduling has some jitter.
-                // That value should not impact latency as the secondary track is started before
-                // its buffer is full, see frameCountToBeReady.
-                size_t frameCount = frameCountToBeReady + 2 * (sourceFrameCount + sinkFrameCount);
-                // The frameCount should also not be smaller than the secondary thread min frame
-                // count
-                size_t minFrameCount = AudioSystem::calculateMinFrameCount(
-                            [&] { Mutex::Autolock _l(secondaryThread->mLock);
-                                  return secondaryThread->latency_l(); }(),
-                            secondaryThread->mNormalFrameCount,
-                            secondaryThread->mSampleRate,
-                            output.sampleRate,
-                            input.speed);
-                frameCount = std::max(frameCount, minFrameCount);
-
-                using namespace std::chrono_literals;
-                auto inChannelMask = audio_channel_mask_out_to_in(input.config.channel_mask);
-                sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
-                                                               output.sampleRate,
-                                                               inChannelMask,
-                                                               input.config.format,
-                                                               frameCount,
-                                                               NULL /* buffer */,
-                                                               (size_t)0 /* bufferSize */,
-                                                               AUDIO_INPUT_FLAG_DIRECT,
-                                                               0ns /* timeout */);
-                status_t status = patchRecord->initCheck();
-                if (status != NO_ERROR) {
-                    ALOGE("Secondary output patchRecord init failed: %d", status);
-                    continue;
-                }
-
-                // TODO: We could check compatibility of the secondaryThread with the PatchTrack
-                // for fast usage: thread has fast mixer, sample rate matches, etc.;
-                // for now, we exclude fast tracks by removing the Fast flag.
-                const audio_output_flags_t outputFlags =
-                        (audio_output_flags_t)(output.flags & ~AUDIO_OUTPUT_FLAG_FAST);
-                sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
-                                                               streamType,
-                                                               output.sampleRate,
-                                                               input.config.channel_mask,
-                                                               input.config.format,
-                                                               frameCount,
-                                                               patchRecord->buffer(),
-                                                               patchRecord->bufferSize(),
-                                                               outputFlags,
-                                                               0ns /* timeout */,
-                                                               frameCountToBeReady);
-                status = patchTrack->initCheck();
-                if (status != NO_ERROR) {
-                    ALOGE("Secondary output patchTrack init failed: %d", status);
-                    continue;
-                }
-                teePatches.push_back({patchRecord, patchTrack});
-                secondaryThread->addPatchTrack(patchTrack);
-                // In case the downstream patchTrack on the secondaryThread temporarily outlives
-                // our created track, ensure the corresponding patchRecord is still alive.
-                patchTrack->setPeerProxy(patchRecord, true /* holdReference */);
-                patchRecord->setPeerProxy(patchTrack, false /* holdReference */);
-            }
-            track->setTeePatches(std::move(teePatches));
+            updateSecondaryOutputsForTrack_l(track.get(), thread, secondaryOutputs);
         }
 
         // move effect chain to this output thread if an effect on same session was waiting
@@ -3441,6 +3381,94 @@
     return nullptr;
 }
 
+void AudioFlinger::updateSecondaryOutputsForTrack_l(
+        PlaybackThread::Track* track,
+        PlaybackThread* thread,
+        const std::vector<audio_io_handle_t> &secondaryOutputs) const {
+    TeePatches teePatches;
+    for (audio_io_handle_t secondaryOutput : secondaryOutputs) {
+        PlaybackThread *secondaryThread = checkPlaybackThread_l(secondaryOutput);
+        if (secondaryThread == nullptr) {
+            ALOGE("no playback thread found for secondary output %d", thread->id());
+            continue;
+        }
+
+        size_t sourceFrameCount = thread->frameCount() * track->sampleRate()
+                                  / thread->sampleRate();
+        size_t sinkFrameCount = secondaryThread->frameCount() * track->sampleRate()
+                                  / secondaryThread->sampleRate();
+        // If the secondary output has just been opened, the first secondaryThread write
+        // will not block as it will fill the empty startup buffer of the HAL,
+        // so a second sink buffer needs to be ready for the immediate next blocking write.
+        // Additionally, have a margin of one main thread buffer as the scheduling jitter
+        // can reorder the writes (eg if thread A&B have the same write intervale,
+        // the scheduler could schedule AB...BA)
+        size_t frameCountToBeReady = 2 * sinkFrameCount + sourceFrameCount;
+        // Total secondary output buffer must be at least as the read frames plus
+        // the margin of a few buffers on both sides in case the
+        // threads scheduling has some jitter.
+        // That value should not impact latency as the secondary track is started before
+        // its buffer is full, see frameCountToBeReady.
+        size_t frameCount = frameCountToBeReady + 2 * (sourceFrameCount + sinkFrameCount);
+        // The frameCount should also not be smaller than the secondary thread min frame
+        // count
+        size_t minFrameCount = AudioSystem::calculateMinFrameCount(
+                    [&] { Mutex::Autolock _l(secondaryThread->mLock);
+                          return secondaryThread->latency_l(); }(),
+                    secondaryThread->mNormalFrameCount,
+                    secondaryThread->mSampleRate,
+                    track->sampleRate(),
+                    track->getSpeed());
+        frameCount = std::max(frameCount, minFrameCount);
+
+        using namespace std::chrono_literals;
+        auto inChannelMask = audio_channel_mask_out_to_in(track->channelMask());
+        sp patchRecord = new RecordThread::PatchRecord(nullptr /* thread */,
+                                                       track->sampleRate(),
+                                                       inChannelMask,
+                                                       track->format(),
+                                                       frameCount,
+                                                       nullptr /* buffer */,
+                                                       (size_t)0 /* bufferSize */,
+                                                       AUDIO_INPUT_FLAG_DIRECT,
+                                                       0ns /* timeout */);
+        status_t status = patchRecord->initCheck();
+        if (status != NO_ERROR) {
+            ALOGE("Secondary output patchRecord init failed: %d", status);
+            continue;
+        }
+
+        // TODO: We could check compatibility of the secondaryThread with the PatchTrack
+        // for fast usage: thread has fast mixer, sample rate matches, etc.;
+        // for now, we exclude fast tracks by removing the Fast flag.
+        const audio_output_flags_t outputFlags =
+                (audio_output_flags_t)(track->getOutputFlags() & ~AUDIO_OUTPUT_FLAG_FAST);
+        sp patchTrack = new PlaybackThread::PatchTrack(secondaryThread,
+                                                       track->streamType(),
+                                                       track->sampleRate(),
+                                                       track->channelMask(),
+                                                       track->format(),
+                                                       frameCount,
+                                                       patchRecord->buffer(),
+                                                       patchRecord->bufferSize(),
+                                                       outputFlags,
+                                                       0ns /* timeout */,
+                                                       frameCountToBeReady);
+        status = patchTrack->initCheck();
+        if (status != NO_ERROR) {
+            ALOGE("Secondary output patchTrack init failed: %d", status);
+            continue;
+        }
+        teePatches.push_back({patchRecord, patchTrack});
+        secondaryThread->addPatchTrack(patchTrack);
+        // In case the downstream patchTrack on the secondaryThread temporarily outlives
+        // our created track, ensure the corresponding patchRecord is still alive.
+        patchTrack->setPeerProxy(patchRecord, true /* holdReference */);
+        patchRecord->setPeerProxy(patchTrack, false /* holdReference */);
+    }
+    track->setTeePatches(std::move(teePatches));
+}
+
 sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
                                     audio_session_t triggerSession,
                                     audio_session_t listenerSession,
@@ -4170,7 +4198,8 @@
         case TransactionCode::SET_LOW_RAM_DEVICE:
         case TransactionCode::SYSTEM_READY:
         case TransactionCode::SET_AUDIO_HAL_PIDS:
-        case TransactionCode::SET_VIBRATOR_INFOS: {
+        case TransactionCode::SET_VIBRATOR_INFOS:
+        case TransactionCode::UPDATE_SECONDARY_OUTPUTS: {
             if (!isServiceUid(IPCThreadState::self()->getCallingUid())) {
                 ALOGW("%s: transaction %d received from PID %d unauthorized UID %d",
                       __func__, code, IPCThreadState::self()->getCallingPid(),
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 4b03d10..b12f52e 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -272,6 +272,9 @@
 
     virtual status_t setVibratorInfos(const std::vector<media::AudioVibratorInfo>& vibratorInfos);
 
+    virtual status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs);
+
     status_t onTransactWrapper(TransactionCode code, const Parcel& data, uint32_t flags,
         const std::function<status_t()>& delegate) override;
 
@@ -775,6 +778,11 @@
 
               ThreadBase *hapticPlaybackThread_l() const;
 
+              void updateSecondaryOutputsForTrack_l(
+                      PlaybackThread::Track* track,
+                      PlaybackThread* thread,
+                      const std::vector<audio_io_handle_t>& secondaryOutputs) const;
+
 
                 void        removeClient_l(pid_t pid);
                 void        removeNotificationClient(pid_t pid);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 0af4c7b..30a2432 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -81,7 +81,8 @@
                                 audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE,
                                 /** default behaviour is to start when there are as many frames
                                   * ready as possible (aka. Buffer is full). */
-                                size_t frameCountToBeReady = SIZE_MAX);
+                                size_t frameCountToBeReady = SIZE_MAX,
+                                float speed = 1.0f);
     virtual             ~Track();
     virtual status_t    initCheck() const;
 
@@ -183,6 +184,9 @@
                    mAudioTrackServerProxy->getUnderrunFrames());
        }
     }
+
+    audio_output_flags_t getOutputFlags() const { return mFlags; }
+    float getSpeed() const { return mSpeed; }
 protected:
     // for numerous
     friend class PlaybackThread;
@@ -311,6 +315,7 @@
     bool                mPauseHwPending = false; // direct/offload track request for thread pause
     audio_output_flags_t mFlags;
     TeePatches  mTeePatches;
+    const float         mSpeed;
 };  // end of Track
 
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d878611..25a19a2 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2425,7 +2425,7 @@
                           channelMask, frameCount,
                           nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
                           sessionId, creatorPid, identity, trackFlags, TrackBase::TYPE_DEFAULT,
-                          portId, SIZE_MAX /*frameCountToBeReady*/);
+                          portId, SIZE_MAX /*frameCountToBeReady*/, speed);
 
         lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
         if (lStatus != NO_ERROR) {
@@ -3321,6 +3321,17 @@
     invalidateTracks_l(streamType);
 }
 
+// getTrackById_l must be called with holding thread lock
+AudioFlinger::PlaybackThread::Track* AudioFlinger::PlaybackThread::getTrackById_l(
+        audio_port_handle_t trackPortId) {
+    for (size_t i = 0; i < mTracks.size(); i++) {
+        if (mTracks[i]->portId() == trackPortId) {
+            return mTracks[i].get();
+        }
+    }
+    return nullptr;
+}
+
 status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
 {
     audio_session_t session = chain->sessionId();
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 17acb16..d4fb995 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1013,6 +1013,8 @@
                     mDownStreamPatch = *patch;
                 }
 
+                PlaybackThread::Track* getTrackById_l(audio_port_handle_t trackId);
+
 protected:
     // updated by readOutputParameters_l()
     size_t                          mNormalFrameCount;  // normal mixer and effects
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 8be7c86..09e5ec5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -624,7 +624,8 @@
             audio_output_flags_t flags,
             track_type type,
             audio_port_handle_t portId,
-            size_t frameCountToBeReady)
+            size_t frameCountToBeReady,
+            float speed)
     :   TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
                   // TODO: Using unsecurePointer() has some associated security pitfalls
                   //       (see declaration for details).
@@ -658,7 +659,8 @@
     mFinalVolume(0.f),
     mResumeToStopping(false),
     mFlushHwPending(false),
-    mFlags(flags)
+    mFlags(flags),
+    mSpeed(speed)
 {
     // client == 0 implies sharedBuffer == 0
     ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -1404,6 +1406,10 @@
 void AudioFlinger::PlaybackThread::Track::setTeePatches(TeePatches teePatches) {
     forEachTeePatchTrack([](auto patchTrack) { patchTrack->destroy(); });
     mTeePatches = std::move(teePatches);
+    if (mState == TrackBase::ACTIVE || mState == TrackBase::RESUMING ||
+            mState == TrackBase::STOPPING_1) {
+        forEachTeePatchTrack([](auto patchTrack) { patchTrack->start(); });
+    }
 }
 
 status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 5f052a5..a904321 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_AUDIOPOLICY_INTERFACE_H
 #define ANDROID_AUDIOPOLICY_INTERFACE_H
 
+#include <media/AudioCommonTypes.h>
 #include <media/AudioDeviceTypeAddr.h>
 #include <media/AudioSystem.h>
 #include <media/AudioPolicy.h>
@@ -453,6 +454,9 @@
     virtual void setSoundTriggerCaptureState(bool active) = 0;
 
     virtual status_t getAudioPort(struct audio_port_v7 *port) = 0;
+
+    virtual status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs) = 0;
 };
 
     // These are the signatures of createAudioPolicyManager/destroyAudioPolicyManager
diff --git a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
index 59876c6..74b3405 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ClientDescriptor.h
@@ -109,6 +109,9 @@
     const std::vector<wp<SwAudioOutputDescriptor>>& getSecondaryOutputs() const {
         return mSecondaryOutputs;
     };
+    void setSecondaryOutputs(std::vector<wp<SwAudioOutputDescriptor>>&& secondaryOutputs) {
+        mSecondaryOutputs = std::move(secondaryOutputs);
+    }
     VolumeSource volumeSource() const { return mVolumeSource; }
     const sp<AudioPolicyMix> getPrimaryMix() const {
         return mPrimaryMix.promote();
@@ -143,7 +146,7 @@
     const product_strategy_t mStrategy;
     const VolumeSource mVolumeSource;
     const audio_output_flags_t mFlags;
-    const std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
+    std::vector<wp<SwAudioOutputDescriptor>> mSecondaryOutputs;
     const wp<AudioPolicyMix> mPrimaryMix;
     /**
      * required for duplicating thread, prevent from removing active client from an output
diff --git a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
index b5885c0..76c35c1 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
@@ -200,6 +200,11 @@
     #
     ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
 
+    #
+    # Reaching 32 bit limit for inclusive criterion out devices: removing
+    #
+    ignored_output_device_values = ['BleSpeaker', 'BleHeadset']
+
     criteria_pattern = re.compile(
         r"\s*V\((?P<type>(?:"+'|'.join(criterion_mapping_table.keys()) + "))_" \
         r"(?P<literal>(?!" + '|'.join(ignored_values) + ")\w*)\s*,\s*" \
@@ -235,7 +240,9 @@
             if criterion_name == "OutputDevicesMaskType":
                 if criterion_literal == "Default":
                     criterion_numerical_value = str(int("0x40000000", 0))
-
+                if criterion_literal in ignored_output_device_values:
+                    logging.info("OutputDevicesMaskType skipping {}".format(criterion_literal))
+                    continue
             try:
                 string_int = int(criterion_numerical_value, 0)
             except ValueError:
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 7185435..c8ddbc6 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -31,6 +31,7 @@
 
 #include <algorithm>
 #include <inttypes.h>
+#include <map>
 #include <math.h>
 #include <set>
 #include <unordered_set>
@@ -5694,6 +5695,7 @@
 
 void AudioPolicyManager::checkSecondaryOutputs() {
     std::set<audio_stream_type_t> streamsToInvalidate;
+    TrackSecondaryOutputsMap trackSecondaryOutputs;
     for (size_t i = 0; i < mOutputs.size(); i++) {
         const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
         for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
@@ -5710,16 +5712,28 @@
                 }
             }
 
-            if (status != OK ||
-                !std::equal(client->getSecondaryOutputs().begin(),
-                            client->getSecondaryOutputs().end(),
-                            secondaryDescs.begin(), secondaryDescs.end())) {
+            if (status != OK) {
                 streamsToInvalidate.insert(client->stream());
+            } else if (!std::equal(
+                    client->getSecondaryOutputs().begin(),
+                    client->getSecondaryOutputs().end(),
+                    secondaryDescs.begin(), secondaryDescs.end())) {
+                std::vector<wp<SwAudioOutputDescriptor>> weakSecondaryDescs;
+                std::vector<audio_io_handle_t> secondaryOutputIds;
+                for (const auto& secondaryDesc : secondaryDescs) {
+                    secondaryOutputIds.push_back(secondaryDesc->mIoHandle);
+                    weakSecondaryDescs.push_back(secondaryDesc);
+                }
+                trackSecondaryOutputs.emplace(client->portId(), secondaryOutputIds);
+                client->setSecondaryOutputs(std::move(weakSecondaryDescs));
             }
         }
     }
+    if (!trackSecondaryOutputs.empty()) {
+        mpClientInterface->updateSecondaryOutputs(trackSecondaryOutputs);
+    }
     for (audio_stream_type_t stream : streamsToInvalidate) {
-        ALOGD("%s Invalidate stream %d due to secondary output change", __func__, stream);
+        ALOGD("%s Invalidate stream %d due to fail getting output for attr", __func__, stream);
         mpClientInterface->invalidateStream(stream);
     }
 }
diff --git a/services/audiopolicy/service/AudioPolicyClientImpl.cpp b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
index 77b5200..cd53073 100644
--- a/services/audiopolicy/service/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyClientImpl.cpp
@@ -291,4 +291,14 @@
     return af->getAudioPort(port);
 }
 
+status_t AudioPolicyService::AudioPolicyClient::updateSecondaryOutputs(
+        const TrackSecondaryOutputsMap& trackSecondaryOutputs) {
+    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+    if (af == nullptr) {
+        ALOGW("%s: could not get AudioFlinger", __func__);
+        return PERMISSION_DENIED;
+    }
+    return af->updateSecondaryOutputs(trackSecondaryOutputs);
+}
+
 } // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 00d9670..6eb33f6 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -735,6 +735,9 @@
 
         status_t getAudioPort(struct audio_port_v7 *port) override;
 
+        status_t updateSecondaryOutputs(
+                const TrackSecondaryOutputsMap& trackSecondaryOutputs) override;
+
      private:
         AudioPolicyService *mAudioPolicyService;
     };
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index e2d7d17..f7b0565 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -134,6 +134,11 @@
     size_t getRoutingUpdatedCounter() const {
         return mRoutingUpdatedUpdateCount; }
 
+    status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs __unused) override {
+        return NO_ERROR;
+    }
+
 private:
     audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
     audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
diff --git a/services/audiopolicy/tests/AudioPolicyTestClient.h b/services/audiopolicy/tests/AudioPolicyTestClient.h
index d289e15..1384864 100644
--- a/services/audiopolicy/tests/AudioPolicyTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyTestClient.h
@@ -91,6 +91,10 @@
     status_t getAudioPort(struct audio_port_v7 *port __unused) override {
         return INVALID_OPERATION;
     };
+    status_t updateSecondaryOutputs(
+            const TrackSecondaryOutputsMap& trackSecondaryOutputs __unused) override {
+        return NO_INIT;
+    }
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 329400d..83d2bc9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -232,6 +232,10 @@
         }
     }
 
+    //Derive primary rear/front cameras, and filter their charactierstics.
+    //This needs to be done after all cameras are enumerated and camera ids are sorted.
+    filterSPerfClassCharacteristics();
+
     return OK;
 }
 
@@ -302,6 +306,45 @@
     filterAPI1SystemCameraLocked(mNormalDeviceIds);
 }
 
+void CameraService::filterSPerfClassCharacteristics() {
+    static int32_t kPerformanceClassLevel =
+            property_get_int32("ro.odm.build.media_performance_class", 0);
+    static bool kIsSPerformanceClass = (kPerformanceClassLevel == 31);
+
+    if (!kIsSPerformanceClass) return;
+
+    // To claim to be S Performance primary cameras, the cameras must be
+    // backward compatible. So performance class primary camera Ids must be API1
+    // compatible.
+    bool firstRearCameraSeen = false, firstFrontCameraSeen = false;
+    for (const auto& cameraId : mNormalDeviceIdsWithoutSystemCamera) {
+        int facing = -1;
+        int orientation = 0;
+        String8 cameraId8(cameraId.c_str());
+        getDeviceVersion(cameraId8, /*out*/&facing, /*out*/&orientation);
+        if (facing == -1) {
+            ALOGE("%s: Unable to get camera device \"%s\" facing", __FUNCTION__, cameraId.c_str());
+            return;
+        }
+
+        if ((facing == hardware::CAMERA_FACING_BACK && !firstRearCameraSeen) ||
+                (facing == hardware::CAMERA_FACING_FRONT && !firstFrontCameraSeen)) {
+            mCameraProviderManager->filterSmallJpegSizes(cameraId);
+
+            if (facing == hardware::CAMERA_FACING_BACK) {
+                firstRearCameraSeen = true;
+            }
+            if (facing == hardware::CAMERA_FACING_FRONT) {
+                firstFrontCameraSeen = true;
+            }
+        }
+
+        if (firstRearCameraSeen && firstFrontCameraSeen) {
+            break;
+        }
+    }
+}
+
 void CameraService::addStates(const String8 id) {
     std::string cameraId(id.c_str());
     hardware::camera::common::V1_0::CameraResourceCost cost;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 22dd0db..d1ed59a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -935,6 +935,11 @@
      */
     void updateCameraNumAndIds();
 
+    /**
+     * Filter camera characteristics for S Performance class primary cameras
+     */
+    void filterSPerfClassCharacteristics();
+
     // Number of camera devices (excluding hidden secure cameras)
     int                 mNumberOfCameras;
     // Number of camera devices (excluding hidden secure cameras and
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 8164df0..bd2e7dc 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2976,7 +2976,7 @@
         const StreamConfiguration &sc = scs[i];
         if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
                 sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
-                sc.width <= limit.width && sc.height <= limit.height) {
+                ((sc.width * sc.height) <= (limit.width * limit.height))) {
             int64_t minFrameDuration = getMinFrameDurationNs(
                     {sc.width, sc.height}, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED);
             if (minFrameDuration > MAX_PREVIEW_RECORD_DURATION_NS) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 6dffc5d..46e5d6d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -1186,6 +1186,17 @@
     return isHiddenPhysicalCameraInternal(cameraId).first;
 }
 
+void CameraProviderManager::filterSmallJpegSizes(const std::string& cameraId) {
+    for (auto& provider : mProviders) {
+        for (auto& deviceInfo : provider->mDevices) {
+            if (deviceInfo->mId == cameraId) {
+                deviceInfo->filterSmallJpegSizes();
+                return;
+            }
+        }
+    }
+}
+
 std::pair<bool, CameraProviderManager::ProviderInfo::DeviceInfo *>
 CameraProviderManager::isHiddenPhysicalCameraInternal(const std::string& cameraId) const {
     auto falseRet = std::make_pair(false, nullptr);
@@ -2562,6 +2573,70 @@
     return res;
 }
 
+void CameraProviderManager::ProviderInfo::DeviceInfo3::filterSmallJpegSizes() {
+    static constexpr int FHD_W = 1920;
+    static constexpr int FHD_H = 1080;
+
+    // Remove small JPEG sizes from available stream configurations
+    std::vector<int32_t> newStreamConfigs;
+    camera_metadata_entry streamConfigs =
+            mCameraCharacteristics.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+    for (size_t i = 0; i < streamConfigs.count; i += 4) {
+        if ((streamConfigs.data.i32[i] == HAL_PIXEL_FORMAT_BLOB) && (streamConfigs.data.i32[i+3] ==
+                ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) &&
+                (streamConfigs.data.i32[i+1] < FHD_W || streamConfigs.data.i32[i+2] < FHD_H)) {
+            continue;
+        }
+        newStreamConfigs.insert(newStreamConfigs.end(), streamConfigs.data.i32 + i,
+                streamConfigs.data.i32 + i + 4);
+    }
+    if (newStreamConfigs.size() > 0) {
+        mCameraCharacteristics.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                newStreamConfigs.data(), newStreamConfigs.size());
+    }
+
+    // Remove small JPEG sizes from available min frame durations
+    std::vector<int64_t> newMinDurations;
+    camera_metadata_entry minDurations =
+            mCameraCharacteristics.find(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+    for (size_t i = 0; i < minDurations.count; i += 4) {
+        if ((minDurations.data.i64[i] == HAL_PIXEL_FORMAT_BLOB) &&
+                (minDurations.data.i64[i+1] < FHD_W || minDurations.data.i64[i+2] < FHD_H)) {
+            continue;
+        }
+        newMinDurations.insert(newMinDurations.end(), minDurations.data.i64 + i,
+                minDurations.data.i64 + i + 4);
+    }
+    if (newMinDurations.size() > 0) {
+        mCameraCharacteristics.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS,
+                newMinDurations.data(), newMinDurations.size());
+    }
+
+    // Remove small JPEG sizes from available stall durations
+    std::vector<int64_t> newStallDurations;
+    camera_metadata_entry stallDurations =
+            mCameraCharacteristics.find(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
+    for (size_t i = 0; i < stallDurations.count; i += 4) {
+        if ((stallDurations.data.i64[i] == HAL_PIXEL_FORMAT_BLOB) &&
+                (stallDurations.data.i64[i+1] < FHD_W || stallDurations.data.i64[i+2] < FHD_H)) {
+            continue;
+        }
+        newStallDurations.insert(newStallDurations.end(), stallDurations.data.i64 + i,
+                stallDurations.data.i64 + i + 4);
+    }
+    if (newStallDurations.size() > 0) {
+        mCameraCharacteristics.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS,
+                newStallDurations.data(), newStallDurations.size());
+    }
+
+    // Re-generate metadata tags that have dependencies on BLOB sizes
+    auto res = addDynamicDepthTags();
+    if (OK != res) {
+        ALOGE("%s: Failed to append dynamic depth tags: %s (%d)", __FUNCTION__,
+                strerror(-res), res);
+    }
+}
+
 status_t CameraProviderManager::ProviderInfo::parseProviderName(const std::string& name,
         std::string *type, uint32_t *id) {
     // Format must be "<type>/<id>"
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 5531dd7..4fde556 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -327,6 +327,8 @@
     status_t getSystemCameraKind(const std::string& id, SystemCameraKind *kind) const;
     bool isHiddenPhysicalCamera(const std::string& cameraId) const;
 
+    void filterSmallJpegSizes(const std::string& cameraId);
+
     static const float kDepthARTolerance;
 private:
     // All private members, unless otherwise noted, expect mInterfaceMutex to be locked before use
@@ -486,6 +488,7 @@
                     bool * /*status*/) {
                 return INVALID_OPERATION;
             }
+            virtual void filterSmallJpegSizes() = 0;
 
             template<class InterfaceT>
             sp<InterfaceT> startDeviceInterface();
@@ -544,6 +547,7 @@
                     const hardware::camera::device::V3_7::StreamConfiguration &configuration,
                     bool *status /*out*/)
                     override;
+            virtual void filterSmallJpegSizes() override;
 
             DeviceInfo3(const std::string& name, const metadata_vendor_id_t tagId,
                     const std::string &id, uint16_t minorVersion,
diff --git a/services/mediametrics/statsd_audiopolicy.cpp b/services/mediametrics/statsd_audiopolicy.cpp
index f44b7c4..3d9376e 100644
--- a/services/mediametrics/statsd_audiopolicy.cpp
+++ b/services/mediametrics/statsd_audiopolicy.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -50,7 +50,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::AudioPolicyData metrics_proto;
+    ::android::stats::mediametrics_message::AudioPolicyData metrics_proto;
 
     // flesh out the protobuf we'll hand off with our data
     //
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index 70a67ae..41efcaa 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -33,7 +33,7 @@
 
 #include "MediaMetricsService.h"
 #include "StringUtils.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -50,7 +50,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::AudioRecordData metrics_proto;
+    ::android::stats::mediametrics_message::AudioRecordData metrics_proto;
 
     // flesh out the protobuf we'll hand off with our data
     //
diff --git a/services/mediametrics/statsd_audiothread.cpp b/services/mediametrics/statsd_audiothread.cpp
index 34cc923..e9b6dd6 100644
--- a/services/mediametrics/statsd_audiothread.cpp
+++ b/services/mediametrics/statsd_audiothread.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -50,7 +50,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::AudioThreadData metrics_proto;
+    ::android::stats::mediametrics_message::AudioThreadData metrics_proto;
 
 #define	MM_PREFIX "android.media.audiothread."
 
diff --git a/services/mediametrics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
index fe269a1..59627ae 100644
--- a/services/mediametrics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -33,7 +33,7 @@
 
 #include "MediaMetricsService.h"
 #include "StringUtils.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -51,7 +51,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::AudioTrackData metrics_proto;
+    ::android::stats::mediametrics_message::AudioTrackData metrics_proto;
 
     // flesh out the protobuf we'll hand off with our data
     //
diff --git a/services/mediametrics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
index 2378f33..4ac5621 100644
--- a/services/mediametrics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -50,7 +50,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::ExtractorData metrics_proto;
+    ::android::stats::mediametrics_message::ExtractorData metrics_proto;
 
     std::string format;
     if (item->getString("android.media.mediaextractor.fmt", &format)) {
@@ -68,17 +68,17 @@
     }
 
     std::string entry_point_string;
-    stats::mediametrics::ExtractorData::EntryPoint entry_point =
-            stats::mediametrics::ExtractorData_EntryPoint_OTHER;
+    stats::mediametrics_message::ExtractorData::EntryPoint entry_point =
+            stats::mediametrics_message::ExtractorData_EntryPoint_OTHER;
     if (item->getString("android.media.mediaextractor.entry", &entry_point_string)) {
       if (entry_point_string == "sdk") {
-        entry_point = stats::mediametrics::ExtractorData_EntryPoint_SDK;
+        entry_point = stats::mediametrics_message::ExtractorData_EntryPoint_SDK;
       } else if (entry_point_string == "ndk-with-jvm") {
-        entry_point = stats::mediametrics::ExtractorData_EntryPoint_NDK_WITH_JVM;
+        entry_point = stats::mediametrics_message::ExtractorData_EntryPoint_NDK_WITH_JVM;
       } else if (entry_point_string == "ndk-no-jvm") {
-        entry_point = stats::mediametrics::ExtractorData_EntryPoint_NDK_NO_JVM;
+        entry_point = stats::mediametrics_message::ExtractorData_EntryPoint_NDK_NO_JVM;
       } else {
-        entry_point = stats::mediametrics::ExtractorData_EntryPoint_OTHER;
+        entry_point = stats::mediametrics_message::ExtractorData_EntryPoint_OTHER;
       }
       metrics_proto.set_entry_point(entry_point);
     }
diff --git a/services/mediametrics/statsd_nuplayer.cpp b/services/mediametrics/statsd_nuplayer.cpp
index 33da81e..bdee1f2 100644
--- a/services/mediametrics/statsd_nuplayer.cpp
+++ b/services/mediametrics/statsd_nuplayer.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
+#include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
 namespace android {
@@ -54,7 +54,7 @@
 
     // the rest into our own proto
     //
-    ::android::stats::mediametrics::NuPlayerData metrics_proto;
+    ::android::stats::mediametrics_message::NuPlayerData metrics_proto;
 
     // flesh out the protobuf we'll hand off with our data
     //
diff --git a/services/mediametrics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
index 6edad7c..1b312b5 100644
--- a/services/mediametrics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -57,8 +57,8 @@
 
     // string kRecorderLogSessionId = "android.media.mediarecorder.log-session-id";
     std::string log_session_id;
-    if (item->getString("android.media.mediarecorder.log_session_id", &log_session_id)) {
-        metrics_proto.set_log_session_id(std::move(log_session_id));
+    if (item->getString("android.media.mediarecorder.log-session-id", &log_session_id)) {
+        metrics_proto.set_log_session_id(log_session_id);
     }
     // string kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
     std::string audio_mime;
@@ -214,8 +214,7 @@
             << " video_bitrate:" << video_bitrate
 
             << " iframe_interval:" << iframe_interval
-            // TODO Recorder - add log_session_id
-            // << " log_session_id:" << log_session_id
+            << " log_session_id:" << log_session_id
             << " }";
     statsdLog->log(android::util::MEDIAMETRICS_RECORDER_REPORTED, log.str());
     return true;
diff --git a/services/mediaresourcemanager/Android.bp b/services/mediaresourcemanager/Android.bp
index db61061..f31202b 100644
--- a/services/mediaresourcemanager/Android.bp
+++ b/services/mediaresourcemanager/Android.bp
@@ -65,6 +65,7 @@
             enabled: true,
         },
     },
+    versions: ["1"],
 }
 
 cc_library {
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/.hash b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/.hash
new file mode 100644
index 0000000..df90ffb
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/.hash
@@ -0,0 +1 @@
+7e198281434e9c679b02265e95c51ea25ed180d3
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserver.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserver.aidl
new file mode 100644
index 0000000..73e0e0b
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserver.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+interface IResourceObserver {
+  oneway void onStatusChanged(android.media.MediaObservableEvent event, int uid, int pid, in android.media.MediaObservableParcel[] observables);
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserverService.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserverService.aidl
new file mode 100644
index 0000000..c2d60af
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/IResourceObserverService.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+interface IResourceObserverService {
+  void registerObserver(android.media.IResourceObserver observer, in android.media.MediaObservableFilter[] filters);
+  void unregisterObserver(android.media.IResourceObserver observer);
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableEvent.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableEvent.aidl
new file mode 100644
index 0000000..a45f0ba
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableEvent.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+@Backing(type="long")
+enum MediaObservableEvent {
+  kBusy = 1,
+  kIdle = 2,
+  kAll = -1,
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableFilter.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableFilter.aidl
new file mode 100644
index 0000000..22ae284
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableFilter.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+parcelable MediaObservableFilter {
+  android.media.MediaObservableType type;
+  android.media.MediaObservableEvent eventFilter;
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableParcel.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableParcel.aidl
new file mode 100644
index 0000000..2270d87
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableParcel.aidl
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+parcelable MediaObservableParcel {
+  android.media.MediaObservableType type;
+  long value = 0;
+}
diff --git a/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableType.aidl b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableType.aidl
new file mode 100644
index 0000000..eab3f3f
--- /dev/null
+++ b/services/mediaresourcemanager/aidl_api/resourceobserver_aidl_interface/1/android/media/MediaObservableType.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media;
+/* @hide */
+@Backing(type="int")
+enum MediaObservableType {
+  kInvalid = 0,
+  kVideoSecureCodec = 1000,
+  kVideoNonSecureCodec = 1001,
+}
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
index df2b4a3..cd11c88 100644
--- a/services/tuner/Android.bp
+++ b/services/tuner/Android.bp
@@ -32,8 +32,8 @@
         ":tv_tuner_aidl",
     ],
     imports: [
-        "android.hardware.common",
-        "android.hardware.common.fmq",
+        "android.hardware.common-V2",
+        "android.hardware.common.fmq-V1",
     ],
 
     backend: {