Add MSD support in getDirectProfilesForAttributes

Enabled an AudioProfile equality comparison that
can ignore the dynamic flags.

Add unit tests for the MSD cases.

Bug: 214971780
Test: atest audiopolicy_tests
Test: atest android.media.audio.cts.DirectAudioProfilesForAttributesTest
Change-Id: Ic72048c9d134c02a79678b8b41f1fb1201ae7086
diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp
index 734fa9c..2170cd8 100644
--- a/media/libaudiofoundation/AudioProfile.cpp
+++ b/media/libaudiofoundation/AudioProfile.cpp
@@ -127,16 +127,17 @@
              "%*s%s\n", spaces, "", audio_encapsulation_type_to_string(mEncapsulationType)));
 }
 
-bool AudioProfile::equals(const sp<AudioProfile>& other) const
+bool AudioProfile::equals(const sp<AudioProfile>& other, bool ignoreDynamicFlags) const
 {
     return other != nullptr &&
            mName.compare(other->mName) == 0 &&
            mFormat == other->getFormat() &&
            mChannelMasks == other->getChannels() &&
            mSamplingRates == other->getSampleRates() &&
-           mIsDynamicFormat == other->isDynamicFormat() &&
-           mIsDynamicChannels == other->isDynamicChannels() &&
-           mIsDynamicRate == other->isDynamicRate() &&
+           (ignoreDynamicFlags ||
+               (mIsDynamicFormat == other->isDynamicFormat() &&
+               mIsDynamicChannels == other->isDynamicChannels() &&
+               mIsDynamicRate == other->isDynamicRate())) &&
            mEncapsulationType == other->getEncapsulationType();
 }
 
@@ -326,10 +327,10 @@
     return false;
 }
 
-bool AudioProfileVector::contains(const sp<AudioProfile>& profile) const
+bool AudioProfileVector::contains(const sp<AudioProfile>& profile, bool ignoreDynamicFlags) const
 {
     for (const auto& audioProfile : *this) {
-        if (audioProfile->equals(profile)) {
+        if (audioProfile->equals(profile, ignoreDynamicFlags)) {
             return true;
         }
     }
@@ -356,6 +357,14 @@
                       });
 }
 
+void AudioProfileVector::addAllValidProfiles(const AudioProfileVector& audioProfiles) {
+    for (const auto& audioProfile : audioProfiles) {
+        if (audioProfile->isValid() && !contains(audioProfile, true /*ignoreDynamicFlags*/)) {
+            add(audioProfile);
+        }
+    }
+}
+
 ConversionResult<AudioProfileVector>
 aidl2legacy_AudioProfileVector(const AudioProfileVector::Aidl& aidl, bool isInput) {
     return convertContainers<AudioProfileVector>(aidl.first, aidl.second,
diff --git a/media/libaudiofoundation/include/media/AudioProfile.h b/media/libaudiofoundation/include/media/AudioProfile.h
index c3a0fb2..79dfd12 100644
--- a/media/libaudiofoundation/include/media/AudioProfile.h
+++ b/media/libaudiofoundation/include/media/AudioProfile.h
@@ -78,7 +78,7 @@
 
     void dump(std::string *dst, int spaces) const;
 
-    bool equals(const sp<AudioProfile>& other) const;
+    bool equals(const sp<AudioProfile>& other, bool ignoreDynamicFlags = false) const;
 
     using Aidl = std::pair<media::audio::common::AudioProfile, media::AudioProfileSys>;
     ConversionResult<Aidl> toParcelable(bool isInput) const;
@@ -139,11 +139,12 @@
     bool hasDynamicProfile() const;
     bool hasDynamicRateFor(audio_format_t format) const;
 
-    bool contains(const sp<AudioProfile>& profile) const;
+    bool contains(const sp<AudioProfile>& profile, bool ignoreDynamicFlags = false) const;
 
     virtual void dump(std::string *dst, int spaces) const;
 
     bool equals(const AudioProfileVector& other) const;
+    void addAllValidProfiles(const AudioProfileVector& audioProfiles);
 
     using Aidl = std::pair<
             std::vector<media::audio::common::AudioProfile>,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index cc36c08..4bade63 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1709,6 +1709,28 @@
     }
 }
 
+bool AudioPolicyManager::msdHasPatchesToAllDevices(const AudioDeviceTypeAddrVector& devices) {
+    DeviceVector devicesToCheck = mOutputDevicesAll.getDevicesFromDeviceTypeAddrVec(devices);
+    AudioPatchCollection msdPatches = getMsdOutputPatches();
+    for (size_t i = 0; i < msdPatches.size(); i++) {
+        const auto& patch = msdPatches[i];
+        for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) {
+            const struct audio_port_config *sink = &patch->mPatch.sinks[j];
+            if (sink->type == AUDIO_PORT_TYPE_DEVICE) {
+                const auto& foundDevice = devicesToCheck.getDevice(
+                    sink->ext.device.type, String8(sink->ext.device.address), AUDIO_FORMAT_DEFAULT);
+                if (foundDevice != nullptr) {
+                    devicesToCheck.remove(foundDevice);
+                    if (devicesToCheck.isEmpty()) {
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
+
 audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
                                                    audio_output_flags_t flags,
                                                    audio_format_t format,
@@ -3895,31 +3917,41 @@
     }
 
     for (const auto& hwModule : mHwModules) {
-        for (const auto& curProfile : hwModule->getOutputProfiles()) {
-            if (!curProfile->asAudioPort()->isDirectOutput()) {
+        // the MSD module checks for different conditions
+        if (strcmp(hwModule->getName(), AUDIO_HARDWARE_MODULE_ID_MSD) == 0) {
+            continue;
+        }
+        for (const auto& outputProfile : hwModule->getOutputProfiles()) {
+            if (!outputProfile->asAudioPort()->isDirectOutput()) {
                 continue;
             }
-            // Allow only profiles that support all the available and routed devices
-            DeviceVector supportedDevices = curProfile->getSupportedDevices();
-            if (supportedDevices.getDevicesFromDeviceTypeAddrVec(devices).size()
+            // allow only profiles that support all the available and routed devices
+            if (outputProfile->getSupportedDevices().getDevicesFromDeviceTypeAddrVec(devices).size()
                     != devices.size()) {
                 continue;
             }
-
-            const auto audioProfiles = curProfile->asAudioPort()->getAudioProfiles();
-            ALOGV("%s: found direct profile (%s) with %zu audio profiles.",
-                __func__, curProfile->getTagName().c_str(), audioProfiles.size());
-            for (const auto& audioProfile : audioProfiles) {
-                if (audioProfile->isValid() && !audioProfilesVector.contains(audioProfile)
-                // TODO - why do we have same PCM format with both dynamic and non dynamic format
-                    && audioProfile->isDynamicFormat()) {
-                    ALOGV("%s: adding audio profile with encoding (%d).",
-                        __func__, audioProfile->getFormat());
-                    audioProfilesVector.add(audioProfile);
-                }
-            }
+            audioProfilesVector.addAllValidProfiles(
+                    outputProfile->asAudioPort()->getAudioProfiles());
         }
     }
+
+    // add the direct profiles from MSD if present and has audio patches to all the output(s)
+    const auto& msdModule = mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+    if (msdModule != nullptr) {
+        if (msdHasPatchesToAllDevices(devices)) {
+            ALOGV("%s: MSD audio patches set to all output devices.", __func__);
+            for (const auto& outputProfile : msdModule->getOutputProfiles()) {
+                if (!outputProfile->asAudioPort()->isDirectOutput()) {
+                    continue;
+                }
+                audioProfilesVector.addAllValidProfiles(
+                        outputProfile->asAudioPort()->getAudioProfiles());
+            }
+        } else {
+            ALOGV("%s: MSD audio patches NOT set to all output devices.", __func__);
+        }
+    }
+
     return NO_ERROR;
 }
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index a145c70..587dd60 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -909,6 +909,7 @@
         PatchBuilder buildMsdPatch(bool msdIsSource, const sp<DeviceDescriptor> &device) const;
         status_t setMsdOutputPatches(const DeviceVector *outputDevices = nullptr);
         void releaseMsdOutputPatches(const DeviceVector& devices);
+        bool msdHasPatchesToAllDevices(const AudioDeviceTypeAddrVector& devices);
 
         // Overload of setDeviceConnectionState()
         status_t setDeviceConnectionState(audio_devices_t deviceType,
@@ -1120,6 +1121,10 @@
         bool isOffloadPossible(const audio_offload_info_t& offloadInfo,
                                bool durationIgnored = false);
 
+        // adds the profiles from the outputProfile to the passed audioProfilesVector
+        // without duplicating them if already present
+        void addPortProfilesToVector(sp<IOProfile> outputProfile,
+                                    AudioProfileVector& audioProfilesVector);
 };
 
 };
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 9c1adc6..693f973 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -373,6 +373,7 @@
   protected:
     void SetUpManagerConfig() override;
     void TearDown() override;
+    AudioProfileVector getDirectProfilesForAttributes(const audio_attributes_t& attr);
 
     sp<DeviceDescriptor> mMsdOutputDevice;
     sp<DeviceDescriptor> mMsdInputDevice;
@@ -502,6 +503,13 @@
     AudioPolicyManagerTest::TearDown();
 }
 
+AudioProfileVector AudioPolicyManagerTestMsd::getDirectProfilesForAttributes(
+                                                    const audio_attributes_t& attr) {
+    AudioProfileVector audioProfilesVector;
+    mManager->getDirectProfilesForAttributes(&attr, audioProfilesVector);
+    return audioProfilesVector;
+}
+
 TEST_P(AudioPolicyManagerTestMsd, InitSuccess) {
     ASSERT_TRUE(mMsdOutputDevice);
     ASSERT_TRUE(mMsdInputDevice);
@@ -642,6 +650,48 @@
     ASSERT_EQ(1, patchCount.deltaFromSnapshot());
 }
 
+TEST_P(AudioPolicyManagerTestMsd, GetDirectProfilesForAttributesWithMsd) {
+    const audio_attributes_t attr = {
+        AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN,
+        AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""};
+
+    // count expected direct profiles for the default device
+    int countDirectProfilesPrimary = 0;
+    const auto& primary = mManager->getConfig().getHwModules()
+            .getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY);
+    for (const auto outputProfile : primary->getOutputProfiles()) {
+        if (outputProfile->asAudioPort()->isDirectOutput()) {
+            countDirectProfilesPrimary += outputProfile->asAudioPort()->getAudioProfiles().size();
+        }
+    }
+
+    // count expected direct profiles for the msd device
+    int countDirectProfilesMsd = 0;
+    const auto& msd = mManager->getConfig().getHwModules()
+            .getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD);
+    for (const auto outputProfile : msd->getOutputProfiles()) {
+        if (outputProfile->asAudioPort()->isDirectOutput()) {
+            countDirectProfilesMsd += outputProfile->asAudioPort()->getAudioProfiles().size();
+        }
+    }
+
+    // before setting up MSD audio patches we only have the primary hal direct profiles
+    ASSERT_EQ(countDirectProfilesPrimary, getDirectProfilesForAttributes(attr).size());
+
+    DeviceVector outputDevices = mManager->getAvailableOutputDevices();
+    // Remove MSD output device to avoid patching to itself
+    outputDevices.remove(mMsdOutputDevice);
+    mManager->setMsdOutputPatches(&outputDevices);
+
+    // after setting up MSD audio patches the MSD direct profiles are added
+    ASSERT_EQ(countDirectProfilesPrimary + countDirectProfilesMsd,
+                getDirectProfilesForAttributes(attr).size());
+
+    mManager->releaseMsdOutputPatches(outputDevices);
+    // releasing the MSD audio patches gets us back to the primary hal direct profiles only
+    ASSERT_EQ(countDirectProfilesPrimary, getDirectProfilesForAttributes(attr).size());
+}
+
 class AudioPolicyManagerTestWithConfigurationFile : public AudioPolicyManagerTest {
 protected:
     void SetUpManagerConfig() override;