APM: update logic of populating profiles for dynamic mix port.
For AIDL HAL, use getAudioPort API to query the profiles for the dynamic
mix ports.
For HIDL HAL, if the HAL supports getAudioPort API, add the profiles of
the device port that contain attributes also reported by getParameters
API. The attributes reported by getParameters API but not in device port
will also be added to mix port.
The reason of using getAudioPort API instead of getParameters is that
getAudioPort API can return well structed audio profiles, which show the
correct relationship between audio format and sample rates, channel
masks.
Test: repo steps from the bug
Test: atest audiofoundation_containers_test
Test: atest audiopolicy_tests
Bug: 284033428
Change-Id: I6d3faf49470514c05fe57be755bb868d74d74dbb
diff --git a/media/libaudiofoundation/AudioContainers.cpp b/media/libaudiofoundation/AudioContainers.cpp
index 3034b9a..e1265cf 100644
--- a/media/libaudiofoundation/AudioContainers.cpp
+++ b/media/libaudiofoundation/AudioContainers.cpp
@@ -130,4 +130,104 @@
return ss.str();
}
+AudioProfileAttributesMultimap createAudioProfilesAttrMap(audio_profile profiles[],
+ uint32_t first,
+ uint32_t last) {
+ AudioProfileAttributesMultimap result;
+ for (uint32_t i = first; i < last; ++i) {
+ SampleRateSet sampleRates(profiles[i].sample_rates,
+ profiles[i].sample_rates + profiles[i].num_sample_rates);
+ ChannelMaskSet channelMasks(profiles[i].channel_masks,
+ profiles[i].channel_masks + profiles[i].num_channel_masks);
+ result.emplace(profiles[i].format, std::make_pair(sampleRates, channelMasks));
+ }
+ return result;
+}
+
+namespace {
+
+void populateAudioProfile(audio_format_t format,
+ const ChannelMaskSet& channelMasks,
+ const SampleRateSet& samplingRates,
+ audio_profile* profile) {
+ profile->format = format;
+ profile->num_channel_masks = 0;
+ for (auto it = channelMasks.begin();
+ it != channelMasks.end() && profile->num_channel_masks < AUDIO_PORT_MAX_CHANNEL_MASKS;
+ ++it) {
+ profile->channel_masks[profile->num_channel_masks++] = *it;
+ }
+ profile->num_sample_rates = 0;
+ for (auto it = samplingRates.begin();
+ it != samplingRates.end() && profile->num_sample_rates < AUDIO_PORT_MAX_SAMPLING_RATES;
+ ++it) {
+ profile->sample_rates[profile->num_sample_rates++] = *it;
+ }
+}
+
+} // namespace
+
+void populateAudioProfiles(const AudioProfileAttributesMultimap& profileAttrs,
+ audio_format_t format,
+ ChannelMaskSet allChannelMasks,
+ SampleRateSet allSampleRates,
+ audio_profile audioProfiles[],
+ uint32_t* numAudioProfiles,
+ uint32_t maxAudioProfiles) {
+ if (*numAudioProfiles >= maxAudioProfiles) {
+ return;
+ }
+
+ const auto lower= profileAttrs.lower_bound(format);
+ const auto upper = profileAttrs.upper_bound(format);
+ SampleRateSet sampleRatesPresent;
+ ChannelMaskSet channelMasksPresent;
+ for (auto it = lower; it != upper && *numAudioProfiles < maxAudioProfiles; ++it) {
+ SampleRateSet srs;
+ std::set_intersection(it->second.first.begin(), it->second.first.end(),
+ allSampleRates.begin(), allSampleRates.end(),
+ std::inserter(srs, srs.begin()));
+ if (srs.empty()) {
+ continue;
+ }
+ ChannelMaskSet cms;
+ std::set_intersection(it->second.second.begin(), it->second.second.end(),
+ allChannelMasks.begin(), allChannelMasks.end(),
+ std::inserter(cms, cms.begin()));
+ if (cms.empty()) {
+ continue;
+ }
+ sampleRatesPresent.insert(srs.begin(), srs.end());
+ channelMasksPresent.insert(cms.begin(), cms.end());
+ populateAudioProfile(it->first, cms, srs,
+ &audioProfiles[(*numAudioProfiles)++]);
+ }
+ if (*numAudioProfiles >= maxAudioProfiles) {
+ ALOGW("%s, too many audio profiles", __func__);
+ return;
+ }
+
+ SampleRateSet srs;
+ std::set_difference(allSampleRates.begin(), allSampleRates.end(),
+ sampleRatesPresent.begin(), sampleRatesPresent.end(),
+ std::inserter(srs, srs.begin()));
+ if (!srs.empty()) {
+ populateAudioProfile(format, allChannelMasks, srs,
+ &audioProfiles[(*numAudioProfiles)++]);
+ }
+ if (*numAudioProfiles >= maxAudioProfiles) {
+ ALOGW("%s, too many audio profiles", __func__);
+ return;
+ }
+ ChannelMaskSet cms;
+ std::set_difference(allChannelMasks.begin(), allChannelMasks.end(),
+ channelMasksPresent.begin(), channelMasksPresent.end(),
+ std::inserter(cms, cms.begin()));
+ if (!cms.empty()) {
+ populateAudioProfile(format, cms, allSampleRates,
+ &audioProfiles[(*numAudioProfiles)++]);
+ }
+
+}
+
} // namespace android
diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp
index 999e263..4a5fb96 100644
--- a/media/libaudiofoundation/AudioProfile.cpp
+++ b/media/libaudiofoundation/AudioProfile.cpp
@@ -383,6 +383,16 @@
}
}
+ChannelMaskSet AudioProfileVector::getSupportedChannelMasks() const {
+ ChannelMaskSet channelMasks;
+ for (const auto& profile : *this) {
+ if (profile->isValid()) {
+ channelMasks.insert(profile->getChannels().begin(), profile->getChannels().end());
+ }
+ }
+ return channelMasks;
+}
+
ConversionResult<AudioProfileVector>
aidl2legacy_AudioProfileVector(const AudioProfileVector::Aidl& aidl, bool isInput) {
return convertContainers<AudioProfileVector>(aidl.first, aidl.second,
diff --git a/media/libaudiofoundation/TEST_MAPPING b/media/libaudiofoundation/TEST_MAPPING
index a4e271e..f7e5b12 100644
--- a/media/libaudiofoundation/TEST_MAPPING
+++ b/media/libaudiofoundation/TEST_MAPPING
@@ -20,5 +20,10 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "audiofoundation_containers_test"
+ }
]
}
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index f22ee40..46fd620 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <functional>
#include <iterator>
+#include <map>
#include <set>
#include <vector>
@@ -34,6 +35,8 @@
using MixerBehaviorSet = std::set<audio_mixer_behavior_t>;
using FormatVector = std::vector<audio_format_t>;
+using AudioProfileAttributesMultimap =
+ std::multimap<audio_format_t, std::pair<SampleRateSet, ChannelMaskSet>>;
const DeviceTypeSet& getAudioDeviceOutAllSet();
const DeviceTypeSet& getAudioDeviceOutAllA2dpSet();
@@ -135,5 +138,49 @@
return deviceTypesToString(deviceTypes);
}
+/**
+ * Create audio profile attributes map by given audio profile array from the range of [first, last).
+ *
+ * @param profiles the array of audio profiles.
+ * @param first the first index of the profile.
+ * @param last the last index of the profile.
+ * @return a multipmap of audio format to pair of corresponding sample rates and channel masks set.
+ */
+AudioProfileAttributesMultimap createAudioProfilesAttrMap(audio_profile profiles[],
+ uint32_t first,
+ uint32_t last);
+
+/**
+ * Populate audio profiles according to given profile attributes, format, channel masks and
+ * sample rates.
+ *
+ * The function will first go over all pairs of channel masks and sample rates that are present in
+ * the profile attributes of the given map. Note that the channel masks and the sample rates that
+ * are not present in the collections of all valid channel masks and all valid sample rates will be
+ * excluded. After that, if there are channel masks and sample rates that present in the all values
+ * collection but not in profile attributes, they will also be place in a new audio profile in the
+ * profile array.
+ *
+ * Note that if the resulting index of the audio profile exceeds the maximum, no new audio profiles
+ * will be placed in the array.
+ *
+ * @param profileAttrs a multimap that contains format and its corresponding channel masks and
+ * sample rates.
+ * @param format the targeted audio format.
+ * @param allChannelMasks all valid channel masks for the format.
+ * @param allSampleRates all valid sample rates for the format.
+ * @param audioProfiles the audio profile array.
+ * @param numAudioProfiles the start index to put audio profile in the array. The value will be
+ * updated if there is new audio profile placed.
+ * @param maxAudioProfiles the maximum number of audio profile.
+ */
+void populateAudioProfiles(const AudioProfileAttributesMultimap& profileAttrs,
+ audio_format_t format,
+ ChannelMaskSet allChannelMasks,
+ SampleRateSet allSampleRates,
+ audio_profile audioProfiles[],
+ uint32_t* numAudioProfiles,
+ uint32_t maxAudioProfiles = AUDIO_PORT_MAX_AUDIO_PROFILES);
+
} // namespace android
diff --git a/media/libaudiofoundation/include/media/AudioProfile.h b/media/libaudiofoundation/include/media/AudioProfile.h
index a668afe..bcde1fe 100644
--- a/media/libaudiofoundation/include/media/AudioProfile.h
+++ b/media/libaudiofoundation/include/media/AudioProfile.h
@@ -149,6 +149,8 @@
bool equals(const AudioProfileVector& other) const;
void addAllValidProfiles(const AudioProfileVector& audioProfiles);
+ ChannelMaskSet getSupportedChannelMasks() const;
+
using Aidl = std::pair<
std::vector<media::audio::common::AudioProfile>,
std::vector<media::AudioProfileSys>>;
diff --git a/media/libaudiofoundation/tests/Android.bp b/media/libaudiofoundation/tests/Android.bp
index 2f4aee0..82c7db7 100644
--- a/media/libaudiofoundation/tests/Android.bp
+++ b/media/libaudiofoundation/tests/Android.bp
@@ -43,3 +43,30 @@
test_suites: ["device-tests"],
}
+
+cc_test {
+ name: "audiofoundation_containers_test",
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ static_libs: [
+ "libaudiofoundation",
+ ],
+
+ header_libs: [
+ "libaudio_system_headers",
+ ],
+
+ srcs: [
+ "audiofoundation_containers_test.cpp",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/media/libaudiofoundation/tests/audiofoundation_containers_test.cpp b/media/libaudiofoundation/tests/audiofoundation_containers_test.cpp
new file mode 100644
index 0000000..967e2ee
--- /dev/null
+++ b/media/libaudiofoundation/tests/audiofoundation_containers_test.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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 <gtest/gtest.h>
+
+#include <media/AudioContainers.h>
+
+namespace android {
+
+const static AudioProfileAttributesMultimap AUDIO_PROFILE_ATTRIBUTES = {
+ {AUDIO_FORMAT_PCM_16_BIT, {{44100, 48000},
+ {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{96000},
+ {AUDIO_CHANNEL_OUT_STEREO}}},
+ {AUDIO_FORMAT_PCM_8_24_BIT, {{48000},
+ {AUDIO_CHANNEL_OUT_STEREO}}}
+};
+
+TEST(PopulateAudioProfilesTest, AllAttributesMatches) {
+ const AudioProfileAttributesMultimap expected = {
+ {AUDIO_FORMAT_PCM_16_BIT, {{44100, 48000},
+ {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{96000},
+ {AUDIO_CHANNEL_OUT_STEREO}}}
+ };
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const SampleRateSet allSampleRates = {44100, 48000, 96000};
+ const ChannelMaskSet allChannelMasks = {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1};
+
+ audio_profile profiles[AUDIO_PORT_MAX_AUDIO_PROFILES];
+ uint32_t numProfiles = 0;
+ populateAudioProfiles(AUDIO_PROFILE_ATTRIBUTES, format, allChannelMasks, allSampleRates,
+ profiles, &numProfiles);
+ ASSERT_EQ(expected, createAudioProfilesAttrMap(profiles, 0, numProfiles));
+}
+
+TEST(PopulateAudioProfilesTest, AttributesNotInAllValues) {
+ const AudioProfileAttributesMultimap expected = {
+ {AUDIO_FORMAT_PCM_16_BIT, {{48000},
+ {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{96000},
+ {AUDIO_CHANNEL_OUT_STEREO}}}
+ };
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const SampleRateSet allSampleRates = {48000, 96000};
+ const ChannelMaskSet allChannelMasks = {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1};
+
+ audio_profile profiles[AUDIO_PORT_MAX_AUDIO_PROFILES];
+ uint32_t numProfiles = 0;
+ populateAudioProfiles(AUDIO_PROFILE_ATTRIBUTES, format, allChannelMasks, allSampleRates,
+ profiles, &numProfiles);
+ ASSERT_EQ(expected, createAudioProfilesAttrMap(profiles, 0, numProfiles));
+}
+
+TEST(PopulateAudioProfilesTest, AllValuesNotInAttributes) {
+ const AudioProfileAttributesMultimap expected = {
+ {AUDIO_FORMAT_PCM_16_BIT, {{48000},
+ {AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{96000},
+ {AUDIO_CHANNEL_OUT_STEREO}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{88200},
+ {AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_CHANNEL_OUT_7POINT1}}},
+ {AUDIO_FORMAT_PCM_16_BIT, {{48000, 88200, 96000},
+ {AUDIO_CHANNEL_OUT_MONO}}}
+ };
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const SampleRateSet allSampleRates = {48000, 88200, 96000};
+ const ChannelMaskSet allChannelMasks =
+ {AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1};
+
+ audio_profile profiles[AUDIO_PORT_MAX_AUDIO_PROFILES];
+ uint32_t numProfiles = 0;
+ populateAudioProfiles(AUDIO_PROFILE_ATTRIBUTES, format, allChannelMasks, allSampleRates,
+ profiles, &numProfiles);
+ ASSERT_EQ(expected, createAudioProfilesAttrMap(profiles, 0, numProfiles));
+}
+
+TEST(PopulateAudioProfilesTest, NoOverflow) {
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const SampleRateSet allSampleRates = {48000, 88200, 96000};
+ const ChannelMaskSet allChannelMasks =
+ {AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_7POINT1};
+
+ audio_profile profiles[AUDIO_PORT_MAX_AUDIO_PROFILES];
+ const uint32_t expectedNumProfiles = 4;
+ for (uint32_t i = 0; i <= AUDIO_PORT_MAX_AUDIO_PROFILES; ++i) {
+ uint32_t numProfiles = 0;
+ populateAudioProfiles(AUDIO_PROFILE_ATTRIBUTES, format, allChannelMasks, allSampleRates,
+ profiles, &numProfiles, i);
+ ASSERT_EQ(std::min(i, expectedNumProfiles), numProfiles);
+ }
+}
+
+} // namespace android