Merge changes I848cd79c,I5a7977be,I33f404e1 am: 8df134eede am: 262e8b0372 am: 20a9f2ab20

Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/2207955

Change-Id: Ie02041ed6373ebac587a7a14749475c2e6c6876e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index d8151f5..60bb4f0 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -41,5 +41,10 @@
         }
       ]
     }
+  ],
+  "postsubmit": [
+    {
+       "name": "audioeffect_analysis"
+    }
   ]
 }
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 1b09173..df1506a 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -176,6 +176,22 @@
 }
 
 cc_test {
+    name: "audioeffect_analysis",
+    defaults: ["libaudioclient_gtests_defaults"],
+    // flag needed for pfft/pffft.hpp
+    cflags: [
+        "-Wno-error=unused-parameter",
+    ],
+    srcs: [
+        "audioeffect_analyser.cpp",
+        "audio_test_utils.cpp",
+    ],
+    static_libs: [
+        "libpffft",
+    ],
+}
+
+cc_test {
     name: "audiorouting_tests",
     defaults: ["libaudioclient_gtests_defaults"],
     srcs: [
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
index 850eb34..1e26ff6 100644
--- a/media/libaudioclient/tests/audio_test_utils.cpp
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -17,11 +17,15 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioTestUtils"
 
+#include <android-base/file.h>
 #include <system/audio_config.h>
 #include <utils/Log.h>
 
 #include "audio_test_utils.h"
 
+#define WAIT_PERIOD_MS 10  // from AudioTrack.cpp
+#define MAX_WAIT_TIME_MS 5000
+
 template <class T>
 constexpr void (*xmlDeleter)(T* t);
 template <>
@@ -37,12 +41,6 @@
     return std::unique_ptr<T, decltype(deleter)>{t, deleter};
 }
 
-// Generates a random string.
-void CreateRandomFile(int& fd) {
-    std::string filename = "/data/local/tmp/record-XXXXXX";
-    fd = mkstemp(filename.data());
-}
-
 void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                                       audio_port_handle_t deviceId) {
     std::unique_lock<std::mutex> lock{mMutex};
@@ -52,11 +50,14 @@
     mCondition.notify_all();
 }
 
-status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb() {
+status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb(audio_port_handle_t expDeviceId) {
     std::unique_lock<std::mutex> lock{mMutex};
-    if (mAudioIo == AUDIO_IO_HANDLE_NONE) {
+    if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
+        (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId)) {
         mCondition.wait_for(lock, std::chrono::milliseconds(500));
-        if (mAudioIo == AUDIO_IO_HANDLE_NONE) return TIMED_OUT;
+        if (mAudioIo == AUDIO_IO_HANDLE_NONE ||
+            (expDeviceId != AUDIO_PORT_HANDLE_NONE && expDeviceId != mDeviceId))
+            return TIMED_OUT;
     }
     return OK;
 }
@@ -167,15 +168,16 @@
 }
 
 status_t AudioPlayback::fillBuffer() {
-    if (PLAY_STARTED != mState && PLAY_STOPPED != mState) return INVALID_OPERATION;
-    int retry = 25;
+    if (PLAY_STARTED != mState) return INVALID_OPERATION;
+    const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+    int counter = 0;
     uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
     size_t nonContig = 0;
     size_t bytesAvailable = mMemCapacity - mBytesUsedSoFar;
     while (bytesAvailable > 0) {
         AudioTrack::Buffer trackBuffer;
         trackBuffer.frameCount = mTrack->frameCount() * 2;
-        status_t status = mTrack->obtainBuffer(&trackBuffer, retry, &nonContig);
+        status_t status = mTrack->obtainBuffer(&trackBuffer, 1, &nonContig);
         if (OK == status) {
             size_t bytesToCopy = std::min(bytesAvailable, trackBuffer.size());
             if (bytesToCopy > 0) {
@@ -184,14 +186,11 @@
             mTrack->releaseBuffer(&trackBuffer);
             mBytesUsedSoFar += bytesToCopy;
             bytesAvailable = mMemCapacity - mBytesUsedSoFar;
-            if (bytesAvailable == 0) {
-                stop();
-            }
+            counter = 0;
         } else if (WOULD_BLOCK == status) {
-            if (mStopPlaying)
-                return OK;
-            else
-                return TIMED_OUT;
+            // if not received a buffer for MAX_WAIT_TIME_MS, something has gone wrong
+            if (counter == maxTries) return TIMED_OUT;
+            counter++;
         }
     }
     return OK;
@@ -199,14 +198,15 @@
 
 status_t AudioPlayback::waitForConsumption(bool testSeek) {
     if (PLAY_STARTED != mState) return INVALID_OPERATION;
-    // in static buffer mode, lets not play clips with duration > 30 sec
-    int retry = 300;
-    // Total number of frames in the input file.
+
+    const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+    int counter = 0;
     size_t totalFrameCount = mMemCapacity / mTrack->frameSize();
-    while (!mStopPlaying && retry > 0) {
-        // Get the total numbers of frames played.
+    while (!mStopPlaying && counter < maxTries) {
         uint32_t currPosition;
         mTrack->getPosition(&currPosition);
+        if (currPosition >= totalFrameCount) counter++;
+
         if (testSeek && (currPosition > totalFrameCount * 0.6)) {
             testSeek = false;
             if (!mTrack->hasStarted()) return BAD_VALUE;
@@ -227,10 +227,9 @@
             if (bufferPosition != setPosition) return BAD_VALUE;
             mTrack->start();
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(100));
-        retry--;
+        std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_PERIOD_MS));
     }
-    if (!mStopPlaying) return TIMED_OUT;
+    if (!mStopPlaying && counter == maxTries) return TIMED_OUT;
     return OK;
 }
 
@@ -246,7 +245,7 @@
 void AudioPlayback::stop() {
     std::unique_lock<std::mutex> lock{mMutex};
     mStopPlaying = true;
-    if (mState != PLAY_STOPPED) {
+    if (mState != PLAY_STOPPED && mState != PLAY_NO_INIT) {
         int32_t msec = 0;
         (void)mTrack->pendingDuration(&msec);
         mTrack->stopAndJoinCallbacks();
@@ -274,7 +273,7 @@
     }
 
     // no more frames to read
-    if (mNumFramesReceived > mNumFramesToRecord || mStopRecording) {
+    if (mNumFramesReceived >= mNumFramesToRecord || mStopRecording) {
         mStopRecording = true;
         return 0;
     }
@@ -369,14 +368,16 @@
 
 AudioCapture::AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
                            audio_channel_mask_t channelMask, audio_input_flags_t flags,
-                           audio_session_t sessionId, AudioRecord::transfer_type transferType)
+                           audio_session_t sessionId, AudioRecord::transfer_type transferType,
+                           const audio_attributes_t* attributes)
     : mInputSource(inputSource),
       mSampleRate(sampleRate),
       mFormat(format),
       mChannelMask(channelMask),
       mFlags(flags),
       mSessionId(sessionId),
-      mTransferType(transferType) {
+      mTransferType(transferType),
+      mAttributes(attributes) {
     mFrameCount = 0;
     mNotificationFrames = 0;
     mNumFramesToRecord = 0;
@@ -389,9 +390,6 @@
     mReceivedCbMarkerCount = 0;
     mState = REC_NO_INIT;
     mStopRecording = false;
-#if RECORD_TO_FILE
-    CreateRandomFile(mOutFileFd);
-#endif
 }
 
 AudioCapture::~AudioCapture() {
@@ -431,19 +429,20 @@
         if (mSampleRate == 48000) {  // test all available constructors
             mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
                                       attributionSource, mFrameCount, nullptr /* callback */,
-                                      mNotificationFrames, mSessionId, mTransferType, mFlags);
+                                      mNotificationFrames, mSessionId, mTransferType, mFlags,
+                                      mAttributes);
         } else {
             mRecord = new AudioRecord(attributionSource);
             status = mRecord->set(mInputSource, mSampleRate, mFormat, mChannelMask, mFrameCount,
                                   nullptr /* callback */, 0 /* notificationFrames */,
                                   false /* canCallJava */, mSessionId, mTransferType, mFlags,
-                                  attributionSource.uid, attributionSource.pid);
+                                  attributionSource.uid, attributionSource.pid, mAttributes);
         }
         if (NO_ERROR != status) return status;
     } else if (mTransferType == AudioRecord::TRANSFER_CALLBACK) {
         mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
                                   attributionSource, mFrameCount, this, mNotificationFrames,
-                                  mSessionId, mTransferType, mFlags);
+                                  mSessionId, mTransferType, mFlags, mAttributes);
     } else {
         ALOGE("Test application is not handling transfer type %s",
               AudioRecord::convertTransferToText(mTransferType));
@@ -460,6 +459,26 @@
     return status;
 }
 
+status_t AudioCapture::setRecordDuration(float durationInSec) {
+    if (REC_READY != mState) {
+        return INVALID_OPERATION;
+    }
+    uint32_t sampleRate = mSampleRate == 0 ? mRecord->getSampleRate() : mSampleRate;
+    mNumFramesToRecord = (sampleRate * durationInSec);
+    return OK;
+}
+
+status_t AudioCapture::enableRecordDump() {
+    if (mOutFileFd != -1) {
+        return INVALID_OPERATION;
+    }
+    TemporaryFile tf("/data/local/tmp");
+    tf.DoNotRemove();
+    mOutFileFd = tf.release();
+    mFileName = std::string{tf.path};
+    return OK;
+}
+
 sp<AudioRecord> AudioCapture::getAudioRecordHandle() {
     return (REC_NO_INIT == mState) ? nullptr : mRecord;
 }
@@ -481,7 +500,7 @@
 status_t AudioCapture::stop() {
     status_t status = OK;
     mStopRecording = true;
-    if (mState != REC_STOPPED) {
+    if (mState != REC_STOPPED && mState != REC_NO_INIT) {
         if (mInputSource != AUDIO_SOURCE_DEFAULT) {
             bool state = false;
             status = AudioSystem::isSourceActive(mInputSource, &state);
@@ -495,81 +514,66 @@
 }
 
 status_t AudioCapture::obtainBuffer(RawBuffer& buffer) {
-    if (REC_STARTED != mState && REC_STOPPED != mState) return INVALID_OPERATION;
-    int retry = 25;
-    AudioRecord::Buffer recordBuffer;
-    recordBuffer.frameCount = mNotificationFrames;
+    if (REC_STARTED != mState) return INVALID_OPERATION;
+    const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+    int counter = 0;
     size_t nonContig = 0;
-    status_t status = mRecord->obtainBuffer(&recordBuffer, retry, &nonContig);
-    if (OK == status) {
-        const int64_t timestampUs =
-                ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
-                mRecord->getSampleRate();
-        RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
-        memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
-        buffer = std::move(buff);
-        mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
-        mRecord->releaseBuffer(&recordBuffer);
-        if (mNumFramesReceived > mNumFramesToRecord) {
-            stop();
+    while (mNumFramesReceived < mNumFramesToRecord) {
+        AudioRecord::Buffer recordBuffer;
+        recordBuffer.frameCount = mNotificationFrames;
+        status_t status = mRecord->obtainBuffer(&recordBuffer, 1, &nonContig);
+        if (OK == status) {
+            const int64_t timestampUs =
+                    ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+                    mRecord->getSampleRate();
+            RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
+            memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
+            buffer = std::move(buff);
+            mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
+            mRecord->releaseBuffer(&recordBuffer);
+            counter = 0;
+        } else if (WOULD_BLOCK == status) {
+            // if not received a buffer for MAX_WAIT_TIME_MS, something has gone wrong
+            if (counter == maxTries) return TIMED_OUT;
+            counter++;
         }
-    } else if (status == WOULD_BLOCK) {
-        if (mStopRecording)
-            return WOULD_BLOCK;
-        else
-            return TIMED_OUT;
     }
     return OK;
 }
 
 status_t AudioCapture::obtainBufferCb(RawBuffer& buffer) {
     if (REC_STARTED != mState) return INVALID_OPERATION;
-    int retry = 10;
+    const int maxTries = MAX_WAIT_TIME_MS / WAIT_PERIOD_MS;
+    int counter = 0;
     std::unique_lock<std::mutex> lock{mMutex};
-    while (mBuffersReceived.empty() && !mStopRecording && retry > 0) {
-        mCondition.wait_for(lock, std::chrono::milliseconds(100));
-        retry--;
+    while (mBuffersReceived.empty() && !mStopRecording && counter < maxTries) {
+        mCondition.wait_for(lock, std::chrono::milliseconds(WAIT_PERIOD_MS));
+        counter++;
     }
     if (!mBuffersReceived.empty()) {
         auto it = mBuffersReceived.begin();
         buffer = std::move(*it);
         mBuffersReceived.erase(it);
     } else {
-        if (retry == 0) return TIMED_OUT;
-        if (mStopRecording)
-            return WOULD_BLOCK;
-        else
-            return UNKNOWN_ERROR;
+        if (!mStopRecording && counter == maxTries) return TIMED_OUT;
     }
     return OK;
 }
 
 status_t AudioCapture::audioProcess() {
     RawBuffer buffer;
-    while (true) {
-        status_t status;
+    status_t status = OK;
+    while (mNumFramesReceived < mNumFramesToRecord && status == OK) {
         if (mTransferType == AudioRecord::TRANSFER_CALLBACK)
             status = obtainBufferCb(buffer);
         else
             status = obtainBuffer(buffer);
-        switch (status) {
-            case OK:
-                if (mOutFileFd > 0) {
-                    const char* ptr =
-                            static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
-                    write(mOutFileFd, ptr, buffer.mCapacity);
-                }
-                break;
-            case WOULD_BLOCK:
-                return OK;
-            case TIMED_OUT:          // "recorder application timed out from receiving buffers"
-            case NO_INIT:            // "recorder not initialized"
-            case INVALID_OPERATION:  // "recorder not started"
-            case UNKNOWN_ERROR:      // "Unknown error"
-            default:
-                return status;
+        if (OK == status && mOutFileFd > 0) {
+            const char* ptr = static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
+            write(mOutFileFd, ptr, buffer.mCapacity);
         }
     }
+    return OK;
 }
 
 status_t listAudioPorts(std::vector<audio_port_v7>& portsVec) {
@@ -613,13 +617,15 @@
 }
 
 status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
-                             audio_devices_t deviceType, audio_port_v7& port) {
+                             audio_devices_t deviceType, const std::string& address,
+                             audio_port_v7& port) {
     std::vector<struct audio_port_v7> ports;
     status_t status = listAudioPorts(ports);
     if (status != OK) return status;
     for (auto i = 0; i < ports.size(); i++) {
         if (ports[i].role == role && ports[i].type == type &&
-            ports[i].ext.device.type == deviceType) {
+            ports[i].ext.device.type == deviceType &&
+            !strncmp(ports[i].ext.device.address, address.c_str(), AUDIO_DEVICE_MAX_ADDRESS_LEN)) {
             port = ports[i];
             return OK;
         }
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
index f35b65d..90c30c2 100644
--- a/media/libaudioclient/tests/audio_test_utils.h
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -34,8 +34,6 @@
 #include <media/AudioRecord.h>
 #include <media/AudioTrack.h>
 
-#define RECORD_TO_FILE 0
-
 using namespace android;
 
 struct MixPort {
@@ -53,11 +51,11 @@
 status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
                                               std::vector<MixPort>& mixPorts,
                                               std::vector<Route>& routes);
-void CreateRandomFile(int& fd);
 status_t listAudioPorts(std::vector<audio_port_v7>& portsVec);
 status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec);
 status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
-                             audio_devices_t deviceType, audio_port_v7& port);
+                             audio_devices_t deviceType, const std::string& address,
+                             audio_port_v7& port);
 status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch);
 status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch);
 bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch);
@@ -76,7 +74,7 @@
     std::condition_variable mCondition;
 
     void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
-    status_t waitForAudioDeviceCb();
+    status_t waitForAudioDeviceCb(audio_port_handle_t expDeviceId = AUDIO_PORT_HANDLE_NONE);
 };
 
 // Simple AudioPlayback class.
@@ -148,7 +146,8 @@
                  audio_channel_mask_t channelMask,
                  audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
                  audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
-                 AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK);
+                 AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK,
+                 const audio_attributes_t* attributes = nullptr);
     ~AudioCapture();
     size_t onMoreData(const AudioRecord::Buffer& buffer) override;
     void onOverrun() override;
@@ -156,6 +155,9 @@
     void onNewPos(uint32_t newPos) override;
     void onNewIAudioRecord() override;
     status_t create();
+    status_t setRecordDuration(float durationInSec);
+    status_t enableRecordDump();
+    std::string getRecordDumpFileName() const { return mFileName; }
     sp<AudioRecord> getAudioRecordHandle();
     status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
                    audio_session_t triggerSession = AUDIO_SESSION_NONE);
@@ -190,11 +192,13 @@
     const audio_input_flags_t mFlags;
     const audio_session_t mSessionId;
     const AudioRecord::transfer_type mTransferType;
+    const audio_attributes_t* mAttributes;
 
     size_t mMaxBytesPerCallback = 2048;
     sp<AudioRecord> mRecord;
     State mState;
     bool mStopRecording;
+    std::string mFileName;
     int mOutFileFd = -1;
 
     std::mutex mMutex;
diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp
new file mode 100644
index 0000000..94accae
--- /dev/null
+++ b/media/libaudioclient/tests/audioeffect_analyser.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AudioEffectAnalyser"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_bassboost.h>
+#include <system/audio_effects/effect_equalizer.h>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "audio_test_utils.h"
+#include "pffft.hpp"
+
+#define CHECK_OK(expr, msg) \
+    mStatus = (expr);       \
+    if (OK != mStatus) {    \
+        mMsg = (msg);       \
+        return;             \
+    }
+
+using namespace android;
+
+constexpr float kDefAmplitude = 0.60f;
+
+constexpr float kPlayBackDurationSec = 1.5;
+constexpr float kCaptureDurationSec = 1.0;
+constexpr float kPrimeDurationInSec = 0.5;
+
+// chosen to safely sample largest center freq of eq bands
+constexpr uint32_t kSamplingFrequency = 48000;
+
+// allows no fmt conversion before fft
+constexpr audio_format_t kFormat = AUDIO_FORMAT_PCM_FLOAT;
+
+// playback and capture are done with channel mask configured to mono.
+// effect analysis should not depend on mask, mono makes it easier.
+
+constexpr int kNPointFFT = 16384;
+constexpr float kBinWidth = (float)kSamplingFrequency / kNPointFFT;
+
+const char* gPackageName = "AudioEffectAnalyser";
+
+static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kCaptureDurationSec,
+              "capture at least, prime, pad, nPointFft size of samples");
+static_assert(kPrimeDurationInSec + 2 * kNPointFFT / kSamplingFrequency < kPlayBackDurationSec,
+              "playback needs to be active during capture");
+
+struct CaptureEnv {
+    // input args
+    uint32_t mSampleRate{kSamplingFrequency};
+    audio_format_t mFormat{kFormat};
+    audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_IN_MONO};
+    float mCaptureDuration{kCaptureDurationSec};
+    // output val
+    status_t mStatus{OK};
+    std::string mMsg;
+    std::string mDumpFileName;
+
+    ~CaptureEnv();
+    void capture();
+};
+
+CaptureEnv::~CaptureEnv() {
+    if (!mDumpFileName.empty()) {
+        std::ifstream f(mDumpFileName);
+        if (f.good()) {
+            f.close();
+            remove(mDumpFileName.c_str());
+        }
+    }
+}
+
+void CaptureEnv::capture() {
+    audio_port_v7 port;
+    CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port),
+             "Could not find port")
+    const auto capture =
+            sp<AudioCapture>::make(AUDIO_SOURCE_REMOTE_SUBMIX, mSampleRate, mFormat, mChannelMask);
+    CHECK_OK(capture->create(), "record creation failed")
+    CHECK_OK(capture->setRecordDuration(mCaptureDuration), "set record duration failed")
+    CHECK_OK(capture->enableRecordDump(), "enable record dump failed")
+    auto cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
+    CHECK_OK(capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture),
+             "addAudioDeviceCallback failed")
+    CHECK_OK(capture->start(), "start recording failed")
+    CHECK_OK(capture->audioProcess(), "recording process failed")
+    CHECK_OK(cbCapture->waitForAudioDeviceCb(), "audio device callback notification timed out");
+    if (port.id != capture->getAudioRecordHandle()->getRoutedDeviceId()) {
+        CHECK_OK(BAD_VALUE, "Capture NOT routed on expected port")
+    }
+    CHECK_OK(getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port),
+             "Could not find port")
+    CHECK_OK(capture->stop(), "record stop failed")
+    mDumpFileName = capture->getRecordDumpFileName();
+}
+
+struct PlaybackEnv {
+    // input args
+    uint32_t mSampleRate{kSamplingFrequency};
+    audio_format_t mFormat{kFormat};
+    audio_channel_mask_t mChannelMask{AUDIO_CHANNEL_OUT_MONO};
+    audio_session_t mSessionId{AUDIO_SESSION_NONE};
+    std::string mRes;
+    // output val
+    status_t mStatus{OK};
+    std::string mMsg;
+
+    void play();
+};
+
+void PlaybackEnv::play() {
+    const auto ap =
+            sp<AudioPlayback>::make(mSampleRate, mFormat, mChannelMask, AUDIO_OUTPUT_FLAG_NONE,
+                                    mSessionId, AudioTrack::TRANSFER_OBTAIN);
+    CHECK_OK(ap->loadResource(mRes.c_str()), "Unable to open Resource")
+    const auto cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+    CHECK_OK(ap->create(), "track creation failed")
+    ap->getAudioTrackHandle()->setVolume(1.0f);
+    CHECK_OK(ap->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback),
+             "addAudioDeviceCallback failed")
+    CHECK_OK(ap->start(), "audio track start failed")
+    CHECK_OK(cbPlayback->waitForAudioDeviceCb(), "audio device callback notification timed out")
+    CHECK_OK(ap->onProcess(), "playback process failed")
+    ap->stop();
+}
+
+void generateMultiTone(const std::vector<int>& toneFrequencies, float samplingFrequency,
+                       float duration, float amplitude, float* buffer, int numSamples) {
+    int totalFrameCount = (samplingFrequency * duration);
+    int limit = std::min(totalFrameCount, numSamples);
+
+    for (auto i = 0; i < limit; i++) {
+        buffer[i] = 0;
+        for (auto j = 0; j < toneFrequencies.size(); j++) {
+            buffer[i] += sin(2 * M_PI * toneFrequencies[j] * i / samplingFrequency);
+        }
+        buffer[i] *= (amplitude / toneFrequencies.size());
+    }
+}
+
+sp<AudioEffect> createEffect(const effect_uuid_t* type,
+                             audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX) {
+    std::string packageName{gPackageName};
+    AttributionSourceState attributionSource;
+    attributionSource.packageName = packageName;
+    attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+    attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+    attributionSource.token = sp<BBinder>::make();
+    sp<AudioEffect> effect = sp<AudioEffect>::make(attributionSource);
+    effect->set(type, nullptr, 0, nullptr, sessionId, AUDIO_IO_HANDLE_NONE, {}, false, false);
+    return effect;
+}
+
+void computeFilterGainsAtTones(float captureDuration, int nPointFft, std::vector<int>& binOffsets,
+                               float* inputMag, float* gaindB, const char* res,
+                               audio_session_t sessionId) {
+    int totalFrameCount = captureDuration * kSamplingFrequency;
+    auto output = pffft::AlignedVector<float>(totalFrameCount);
+    auto fftOutput = pffft::AlignedVector<float>(nPointFft);
+    PlaybackEnv argsP;
+    argsP.mRes = std::string{res};
+    argsP.mSessionId = sessionId;
+    CaptureEnv argsR;
+    argsR.mCaptureDuration = captureDuration;
+    std::thread playbackThread(&PlaybackEnv::play, &argsP);
+    std::thread captureThread(&CaptureEnv::capture, &argsR);
+    captureThread.join();
+    playbackThread.join();
+    ASSERT_EQ(OK, argsR.mStatus) << argsR.mMsg;
+    ASSERT_EQ(OK, argsP.mStatus) << argsP.mMsg;
+    ASSERT_FALSE(argsR.mDumpFileName.empty()) << "recorded not written to file";
+    std::ifstream fin(argsR.mDumpFileName, std::ios::in | std::ios::binary);
+    fin.read((char*)output.data(), totalFrameCount * sizeof(output[0]));
+    fin.close();
+    PFFFT_Setup* handle = pffft_new_setup(nPointFft, PFFFT_REAL);
+    // ignore first few samples. This is to not analyse until audio track is re-routed to remote
+    // submix source, also for the effect filter response to reach steady-state (priming / pruning
+    // samples).
+    int rerouteOffset = kPrimeDurationInSec * kSamplingFrequency;
+    pffft_transform_ordered(handle, output.data() + rerouteOffset, fftOutput.data(), nullptr,
+                            PFFFT_FORWARD);
+    pffft_destroy_setup(handle);
+    for (auto i = 0; i < binOffsets.size(); i++) {
+        auto k = binOffsets[i];
+        auto outputMag = sqrt((fftOutput[k * 2] * fftOutput[k * 2]) +
+                              (fftOutput[k * 2 + 1] * fftOutput[k * 2 + 1]));
+        gaindB[i] = 20 * log10(outputMag / inputMag[i]);
+    }
+}
+
+std::tuple<int, int> roundToFreqCenteredToFftBin(float binWidth, float freq) {
+    int bin_index = std::round(freq / binWidth);
+    int cfreq = std::round(bin_index * binWidth);
+    return std::make_tuple(bin_index, cfreq);
+}
+
+TEST(AudioEffectTest, CheckEqualizerEffect) {
+    audio_session_t sessionId =
+            (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+    sp<AudioEffect> equalizer = createEffect(SL_IID_EQUALIZER, sessionId);
+    ASSERT_EQ(OK, equalizer->initCheck());
+    ASSERT_EQ(NO_ERROR, equalizer->setEnabled(true));
+    if ((equalizer->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
+        GTEST_SKIP() << "effect processed output inaccessible, skipping test";
+    }
+#define MAX_PARAMS 64
+    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + MAX_PARAMS];
+    effect_param_t* eqParam = (effect_param_t*)(&buf32);
+
+    // get num of presets
+    eqParam->psize = sizeof(uint32_t);
+    eqParam->vsize = sizeof(uint16_t);
+    *(int32_t*)eqParam->data = EQ_PARAM_GET_NUM_OF_PRESETS;
+    EXPECT_EQ(0, equalizer->getParameter(eqParam));
+    EXPECT_EQ(0, eqParam->status);
+    int numPresets = *((uint16_t*)((int32_t*)eqParam->data + 1));
+
+    // get num of bands
+    eqParam->psize = sizeof(uint32_t);
+    eqParam->vsize = sizeof(uint16_t);
+    *(int32_t*)eqParam->data = EQ_PARAM_NUM_BANDS;
+    EXPECT_EQ(0, equalizer->getParameter(eqParam));
+    EXPECT_EQ(0, eqParam->status);
+    int numBands = *((uint16_t*)((int32_t*)eqParam->data + 1));
+
+    const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
+
+    // get band center frequencies
+    std::vector<int> centerFrequencies;
+    std::vector<int> binOffsets;
+    for (auto i = 0; i < numBands; i++) {
+        eqParam->psize = sizeof(uint32_t) * 2;
+        eqParam->vsize = sizeof(uint32_t);
+        *(int32_t*)eqParam->data = EQ_PARAM_CENTER_FREQ;
+        *((uint16_t*)((int32_t*)eqParam->data + 1)) = i;
+        EXPECT_EQ(0, equalizer->getParameter(eqParam));
+        EXPECT_EQ(0, eqParam->status);
+        float cfreq = *((int32_t*)eqParam->data + 2) / 1000;  // milli hz
+        // pick frequency close to bin center frequency
+        auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, cfreq);
+        centerFrequencies.push_back(bin_freq);
+        binOffsets.push_back(bin_index);
+    }
+
+    // input for effect module
+    auto input = pffft::AlignedVector<float>(totalFrameCount);
+    generateMultiTone(centerFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
+                      input.data(), totalFrameCount);
+    auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
+    PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
+    pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
+    pffft_destroy_setup(handle);
+    float inputMag[numBands];
+    for (auto i = 0; i < numBands; i++) {
+        auto k = binOffsets[i];
+        inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+                           (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+    }
+    TemporaryFile tf("/data/local/tmp");
+    close(tf.release());
+    std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
+    fout.write((char*)input.data(), input.size() * sizeof(input[0]));
+    fout.close();
+
+    float expGaindB[numBands], actGaindB[numBands];
+
+    std::string msg = "";
+    int numPresetsOk = 0;
+    for (auto preset = 0; preset < numPresets; preset++) {
+        // set preset
+        eqParam->psize = sizeof(uint32_t);
+        eqParam->vsize = sizeof(uint32_t);
+        *(int32_t*)eqParam->data = EQ_PARAM_CUR_PRESET;
+        *((uint16_t*)((int32_t*)eqParam->data + 1)) = preset;
+        EXPECT_EQ(0, equalizer->setParameter(eqParam));
+        EXPECT_EQ(0, eqParam->status);
+        // get preset gains
+        eqParam->psize = sizeof(uint32_t);
+        eqParam->vsize = (numBands + 1) * sizeof(uint32_t);
+        *(int32_t*)eqParam->data = EQ_PARAM_PROPERTIES;
+        EXPECT_EQ(0, equalizer->getParameter(eqParam));
+        EXPECT_EQ(0, eqParam->status);
+        t_equalizer_settings* settings =
+                reinterpret_cast<t_equalizer_settings*>((int32_t*)eqParam->data + 1);
+        EXPECT_EQ(preset, settings->curPreset);
+        EXPECT_EQ(numBands, settings->numBands);
+        for (auto i = 0; i < numBands; i++) {
+            expGaindB[i] = ((int16_t)settings->bandLevels[i]) / 100.0f;  // gain in milli bels
+        }
+        memset(actGaindB, 0, sizeof(actGaindB));
+        ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
+                                                          binOffsets, inputMag, actGaindB, tf.path,
+                                                          sessionId));
+        bool isOk = true;
+        for (auto i = 0; i < numBands - 1; i++) {
+            auto diffA = expGaindB[i] - expGaindB[i + 1];
+            auto diffB = actGaindB[i] - actGaindB[i + 1];
+            if (diffA == 0 && fabs(diffA - diffB) > 1.0f) {
+                msg += (android::base::StringPrintf(
+                        "For eq preset : %d, between bands %d and %d, expected relative gain is : "
+                        "%f, got relative gain is : %f, error : %f \n",
+                        preset, i, i + 1, diffA, diffB, diffA - diffB));
+                isOk = false;
+            } else if (diffA * diffB < 0) {
+                msg += (android::base::StringPrintf(
+                        "For eq preset : %d, between bands %d and %d, expected relative gain and "
+                        "seen relative gain are of opposite signs \n. Expected relative gain is : "
+                        "%f, seen relative gain is : %f \n",
+                        preset, i, i + 1, diffA, diffB));
+                isOk = false;
+            }
+        }
+        if (isOk) numPresetsOk++;
+    }
+    EXPECT_EQ(numPresetsOk, numPresets) << msg;
+}
+
+TEST(AudioEffectTest, CheckBassBoostEffect) {
+    audio_session_t sessionId =
+            (audio_session_t)AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
+    sp<AudioEffect> bassboost = createEffect(SL_IID_BASSBOOST, sessionId);
+    ASSERT_EQ(OK, bassboost->initCheck());
+    ASSERT_EQ(NO_ERROR, bassboost->setEnabled(true));
+    if ((bassboost->descriptor().flags & EFFECT_FLAG_HW_ACC_MASK) != 0) {
+        GTEST_SKIP() << "effect processed output inaccessible, skipping test";
+    }
+    int32_t buf32[sizeof(effect_param_t) / sizeof(int32_t) + MAX_PARAMS];
+    effect_param_t* bbParam = (effect_param_t*)(&buf32);
+
+    bbParam->psize = sizeof(int32_t);
+    bbParam->vsize = sizeof(int32_t);
+    *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH_SUPPORTED;
+    EXPECT_EQ(0, bassboost->getParameter(bbParam));
+    EXPECT_EQ(0, bbParam->status);
+    bool strengthSupported = *((int32_t*)bbParam->data + 1);
+
+    const int totalFrameCount = kSamplingFrequency * kPlayBackDurationSec;
+
+    // selecting bass frequency, speech tone (for relative gain)
+    std::vector<int> testFrequencies{100, 1200};
+    std::vector<int> binOffsets;
+    for (auto i = 0; i < testFrequencies.size(); i++) {
+        // pick frequency close to bin center frequency
+        auto [bin_index, bin_freq] = roundToFreqCenteredToFftBin(kBinWidth, testFrequencies[i]);
+        testFrequencies[i] = bin_freq;
+        binOffsets.push_back(bin_index);
+    }
+
+    // input for effect module
+    auto input = pffft::AlignedVector<float>(totalFrameCount);
+    generateMultiTone(testFrequencies, kSamplingFrequency, kPlayBackDurationSec, kDefAmplitude,
+                      input.data(), totalFrameCount);
+    auto fftInput = pffft::AlignedVector<float>(kNPointFFT);
+    PFFFT_Setup* handle = pffft_new_setup(kNPointFFT, PFFFT_REAL);
+    pffft_transform_ordered(handle, input.data(), fftInput.data(), nullptr, PFFFT_FORWARD);
+    pffft_destroy_setup(handle);
+    float inputMag[testFrequencies.size()];
+    for (auto i = 0; i < testFrequencies.size(); i++) {
+        auto k = binOffsets[i];
+        inputMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
+                           (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
+    }
+    TemporaryFile tf("/data/local/tmp");
+    close(tf.release());
+    std::ofstream fout(tf.path, std::ios::out | std::ios::binary);
+    fout.write((char*)input.data(), input.size() * sizeof(input[0]));
+    fout.close();
+
+    float gainWithOutFilter[testFrequencies.size()];
+    memset(gainWithOutFilter, 0, sizeof(gainWithOutFilter));
+    ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT, binOffsets,
+                                                      inputMag, gainWithOutFilter, tf.path,
+                                                      AUDIO_SESSION_OUTPUT_MIX));
+    float diffA = gainWithOutFilter[0] - gainWithOutFilter[1];
+    float prevGain = -100.f;
+    for (auto strength = 150; strength < 1000; strength += strengthSupported ? 150 : 1000) {
+        // configure filter strength
+        if (strengthSupported) {
+            bbParam->psize = sizeof(int32_t);
+            bbParam->vsize = sizeof(int16_t);
+            *(int32_t*)bbParam->data = BASSBOOST_PARAM_STRENGTH;
+            *((int16_t*)((int32_t*)bbParam->data + 1)) = strength;
+            EXPECT_EQ(0, bassboost->setParameter(bbParam));
+            EXPECT_EQ(0, bbParam->status);
+        }
+        float gainWithFilter[testFrequencies.size()];
+        memset(gainWithFilter, 0, sizeof(gainWithFilter));
+        ASSERT_NO_FATAL_FAILURE(computeFilterGainsAtTones(kCaptureDurationSec, kNPointFFT,
+                                                          binOffsets, inputMag, gainWithFilter,
+                                                          tf.path, sessionId));
+        float diffB = gainWithFilter[0] - gainWithFilter[1];
+        EXPECT_GT(diffB, diffA) << "bassboost effect not seen";
+        EXPECT_GE(diffB, prevGain) << "increase in boost strength causing fall in gain";
+        prevGain = diffB;
+    }
+}
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index 4bd81c8..2c5fcd7 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -46,10 +46,10 @@
         attributes.usage = AUDIO_USAGE_MEDIA;
         attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
         attributes.flags = flags[i];
-        sp<AudioPlayback> ap = sp<AudioPlayback>::make(
-                0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
-                AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN,
-                &attributes);
+        sp<AudioPlayback> ap = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+                                                       AUDIO_CHANNEL_OUT_STEREO,
+                                                       AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE,
+                                                       AudioTrack::TRANSFER_OBTAIN, &attributes);
         ASSERT_NE(nullptr, ap);
         ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
                 << "Unable to open Resource";
@@ -77,46 +77,186 @@
     }
 }
 
-TEST(AudioTrackTest, TestRemoteSubmix) {
-    std::vector<std::string> attachedDevices;
-    std::vector<MixPort> mixPorts;
-    std::vector<Route> routes;
-    EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
-    bool hasFlag = false;
-    for (int j = 0; j < attachedDevices.size() && !hasFlag; j++) {
-        if (attachedDevices[j].find("Remote Submix") != -1) hasFlag = true;
+TEST(AudioTrackTest, DefaultRoutingTest) {
+    audio_port_v7 port;
+    if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                  AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
+        GTEST_SKIP() << "remote submix in device not connected";
     }
-    if (!hasFlag) GTEST_SKIP() << " Device does not have Remote Submix port.";
-    sp<AudioCapture> capture = new AudioCapture(AUDIO_SOURCE_REMOTE_SUBMIX, 48000,
-                                                AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
-    ASSERT_NE(nullptr, capture);
-    ASSERT_EQ(OK, capture->create()) << "record creation failed";
 
+    // create record instance
+    sp<AudioCapture> capture = sp<AudioCapture>::make(
+            AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+    ASSERT_NE(nullptr, capture);
+    EXPECT_EQ(OK, capture->create()) << "record creation failed";
+    sp<OnAudioDeviceUpdateNotifier> cbCapture = sp<OnAudioDeviceUpdateNotifier>::make();
+    EXPECT_EQ(OK, capture->getAudioRecordHandle()->addAudioDeviceCallback(cbCapture));
+
+    // create playback instance
     sp<AudioPlayback> playback = sp<AudioPlayback>::make(
             48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
             AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
     ASSERT_NE(nullptr, playback);
     ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
             << "Unable to open Resource";
-    ASSERT_EQ(OK, playback->create()) << "track creation failed";
+    EXPECT_EQ(OK, playback->create()) << "track creation failed";
+    sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+    EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
 
-    audio_port_v7 port;
-    status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
-                                          AUDIO_DEVICE_IN_REMOTE_SUBMIX, port);
-    EXPECT_EQ(OK, status) << "Could not find port";
-
+    // capture should be routed to submix in port
     EXPECT_EQ(OK, capture->start()) << "start recording failed";
+    EXPECT_EQ(OK, cbCapture->waitForAudioDeviceCb());
     EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
             << "Capture NOT routed on expected port";
 
-    status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
-                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, port);
+    // capture start should create submix out port
+    status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                          AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
     EXPECT_EQ(OK, status) << "Could not find port";
 
+    // playback should be routed to submix out as long as capture is active
     EXPECT_EQ(OK, playback->start()) << "audio track start failed";
-    EXPECT_EQ(OK, playback->onProcess());
-    ASSERT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+    EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
+    EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
             << "Playback NOT routed on expected port";
+
     capture->stop();
     playback->stop();
 }
+
+class AudioRoutingTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        audio_port_v7 port;
+        if (OK != getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                      AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port)) {
+            GTEST_SKIP() << "remote submix in device not connected";
+        }
+        uint32_t mixType = MIX_TYPE_PLAYERS;
+        uint32_t mixFlag = MIX_ROUTE_FLAG_LOOP_BACK;
+        audio_devices_t deviceType = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        AudioMixMatchCriterion criterion(AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT,
+                                         RULE_MATCH_ATTRIBUTE_USAGE);
+        std::vector<AudioMixMatchCriterion> criteria{criterion};
+        audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+        config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+        config.format = AUDIO_FORMAT_PCM_16_BIT;
+        config.sample_rate = 48000;
+        AudioMix mix(criteria, mixType, config, mixFlag, String8{mAddress.c_str()}, 0);
+        mix.mDeviceType = deviceType;
+        mMixes.push(mix);
+        if (OK == AudioSystem::registerPolicyMixes(mMixes, true)) {
+            mPolicyMixRegistered = true;
+        }
+        ASSERT_TRUE(mPolicyMixRegistered) << "register policy mix failed";
+    }
+
+    void TearDown() override {
+        if (mPolicyMixRegistered) {
+            EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
+        }
+    }
+
+    bool mPolicyMixRegistered{false};
+    std::string mAddress{"mix_1"};
+    Vector<AudioMix> mMixes;
+};
+
+TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) {
+    audio_port_v7 port, port_mix;
+    // expect legacy submix in port to be connected
+    status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                          AUDIO_DEVICE_IN_REMOTE_SUBMIX, "0", port);
+    EXPECT_EQ(OK, status) << "Could not find port";
+
+    // as policy mix is registered, expect submix in port with mAddress to be connected
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
+    EXPECT_EQ(OK, status) << "Could not find port";
+
+    // create playback instance
+    sp<AudioPlayback> playback = sp<AudioPlayback>::make(
+            48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+            AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
+    ASSERT_NE(nullptr, playback);
+    ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+            << "Unable to open Resource";
+    EXPECT_EQ(OK, playback->create()) << "track creation failed";
+    sp<OnAudioDeviceUpdateNotifier> cbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+    EXPECT_EQ(OK, playback->getAudioTrackHandle()->addAudioDeviceCallback(cbPlayback));
+
+    // create capture instances on different ports
+    sp<AudioCapture> captureA = sp<AudioCapture>::make(
+            AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+    ASSERT_NE(nullptr, captureA);
+    EXPECT_EQ(OK, captureA->create()) << "record creation failed";
+    sp<OnAudioDeviceUpdateNotifier> cbCaptureA = sp<OnAudioDeviceUpdateNotifier>::make();
+    EXPECT_EQ(OK, captureA->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureA));
+
+    audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+    attr.source = AUDIO_SOURCE_REMOTE_SUBMIX;
+    sprintf(attr.tags, "addr=%s", mAddress.c_str());
+    sp<AudioCapture> captureB = sp<AudioCapture>::make(
+            AUDIO_SOURCE_REMOTE_SUBMIX, 48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
+            AUDIO_INPUT_FLAG_NONE, AUDIO_SESSION_ALLOCATE, AudioRecord::TRANSFER_CALLBACK, &attr);
+    ASSERT_NE(nullptr, captureB);
+    EXPECT_EQ(OK, captureB->create()) << "record creation failed";
+    sp<OnAudioDeviceUpdateNotifier> cbCaptureB = sp<OnAudioDeviceUpdateNotifier>::make();
+    EXPECT_EQ(OK, captureB->getAudioRecordHandle()->addAudioDeviceCallback(cbCaptureB));
+
+    // launch
+    EXPECT_EQ(OK, captureA->start()) << "start recording failed";
+    EXPECT_EQ(OK, cbCaptureA->waitForAudioDeviceCb());
+    EXPECT_EQ(port.id, captureA->getAudioRecordHandle()->getRoutedDeviceId())
+            << "Capture NOT routed on expected port";
+
+    EXPECT_EQ(OK, captureB->start()) << "start recording failed";
+    EXPECT_EQ(OK, cbCaptureB->waitForAudioDeviceCb());
+    EXPECT_EQ(port_mix.id, captureB->getAudioRecordHandle()->getRoutedDeviceId())
+            << "Capture NOT routed on expected port";
+
+    // as record started, expect submix out ports to be connected
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
+    EXPECT_EQ(OK, status) << "unexpected submix out port found";
+
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
+    EXPECT_EQ(OK, status) << "Could not find port";
+
+    // check if playback routed to desired port
+    EXPECT_EQ(OK, playback->start());
+    EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb());
+    EXPECT_EQ(port_mix.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+            << "Playback NOT routed on expected port";
+
+    captureB->stop();
+
+    // check if mAddress submix out is disconnected as capture session is stopped
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mAddress, port_mix);
+    EXPECT_NE(OK, status) << "unexpected submix in port found";
+
+    // check if legacy submix out is connected
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX, "0", port);
+    EXPECT_EQ(OK, status) << "port not found";
+
+    // unregister policy
+    EXPECT_EQ(OK, AudioSystem::registerPolicyMixes(mMixes, false));
+    mPolicyMixRegistered = false;
+
+    // as policy mix is unregistered, expect submix in port with mAddress to be disconnected
+    status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+                                 AUDIO_DEVICE_IN_REMOTE_SUBMIX, mAddress, port_mix);
+    EXPECT_NE(OK, status) << "unexpected submix in port found";
+
+    playback->onProcess();
+    // as captureA is active, it should re route to legacy submix
+    EXPECT_EQ(OK, cbPlayback->waitForAudioDeviceCb(port.id));
+    EXPECT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+            << "Playback NOT routed on expected port";
+
+    captureA->stop();
+    playback->stop();
+}