Merge "audio: Allow "dynamic" profiles for device ports" into main am: e3dcd057d9
am: e8825cf308
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2775704 Change-Id: I079cb19cdb1dc971ff1783b2f919e2a0f826c4f2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
b6d0d44db3
9 changed files with 335 additions and 150 deletions
|
@ -105,15 +105,11 @@ static AudioPort createPort(int32_t id, const std::string& name, int32_t flags,
|
|||
return port;
|
||||
}
|
||||
|
||||
static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout,
|
||||
int32_t sampleRate, int32_t flags, bool isInput,
|
||||
const AudioPortExt& ext) {
|
||||
static AudioPortConfig createDynamicPortConfig(int32_t id, int32_t portId, int32_t flags,
|
||||
bool isInput, const AudioPortExt& ext) {
|
||||
AudioPortConfig config;
|
||||
config.id = id;
|
||||
config.portId = portId;
|
||||
config.sampleRate = Int{.value = sampleRate};
|
||||
config.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
|
||||
config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType};
|
||||
config.gain = AudioGainConfig();
|
||||
config.flags = isInput ? AudioIoFlags::make<AudioIoFlags::Tag::input>(flags)
|
||||
: AudioIoFlags::make<AudioIoFlags::Tag::output>(flags);
|
||||
|
@ -121,6 +117,16 @@ static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmT
|
|||
return config;
|
||||
}
|
||||
|
||||
static AudioPortConfig createPortConfig(int32_t id, int32_t portId, PcmType pcmType, int32_t layout,
|
||||
int32_t sampleRate, int32_t flags, bool isInput,
|
||||
const AudioPortExt& ext) {
|
||||
AudioPortConfig config = createDynamicPortConfig(id, portId, flags, isInput, ext);
|
||||
config.sampleRate = Int{.value = sampleRate};
|
||||
config.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
|
||||
config.format = AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = pcmType};
|
||||
return config;
|
||||
}
|
||||
|
||||
static AudioRoute createRoute(const std::vector<AudioPort>& sources, const AudioPort& sink) {
|
||||
AudioRoute route;
|
||||
route.sinkPortId = sink.id;
|
||||
|
@ -147,8 +153,7 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
|
|||
// * "primary output", PRIMARY, 1 max open, 1 max active stream
|
||||
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// * "primary input", 1 max open, 1 max active stream
|
||||
// - profile PCM 16-bit; MONO, STEREO;
|
||||
// 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// * "telephony_tx", 1 max open, 1 max active stream
|
||||
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// * "telephony_rx", 1 max open, 1 max active stream
|
||||
|
@ -164,11 +169,11 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
|
|||
// "FM Tuner" -> "fm_tuner"
|
||||
//
|
||||
// Initial port configs:
|
||||
// * "Speaker" device port: PCM 16-bit; STEREO; 48000
|
||||
// * "Built-In Mic" device port: PCM 16-bit; MONO; 48000
|
||||
// * "Telephony Tx" device port: PCM 16-bit; MONO; 48000
|
||||
// * "Telephony Rx" device port: PCM 16-bit; MONO; 48000
|
||||
// * "FM Tuner" device port: PCM 16-bit; STEREO; 48000
|
||||
// * "Speaker" device port: dynamic configuration
|
||||
// * "Built-In Mic" device port: dynamic configuration
|
||||
// * "Telephony Tx" device port: dynamic configuration
|
||||
// * "Telephony Rx" device port: dynamic configuration
|
||||
// * "FM Tuner" device port: dynamic configuration
|
||||
//
|
||||
std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
|
@ -186,9 +191,8 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
|||
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
|
||||
c.ports.push_back(speakerOutDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(speakerOutDevice.id, speakerOutDevice.id, PcmType::INT_16_BIT,
|
||||
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
|
||||
createDynamicPortConfig(speakerOutDevice.id, speakerOutDevice.id, 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_SPEAKER, 0)));
|
||||
|
||||
AudioPort micInDevice =
|
||||
createPort(c.nextPortId++, "Built-In Mic", 0, true,
|
||||
|
@ -196,35 +200,31 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
|||
1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
|
||||
c.ports.push_back(micInDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(micInDevice.id, micInDevice.id, PcmType::INT_16_BIT,
|
||||
AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
|
||||
createDynamicPortConfig(micInDevice.id, micInDevice.id, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_MICROPHONE, 0)));
|
||||
|
||||
AudioPort telephonyTxOutDevice =
|
||||
createPort(c.nextPortId++, "Telephony Tx", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0));
|
||||
c.ports.push_back(telephonyTxOutDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id,
|
||||
PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
|
||||
false, createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)));
|
||||
createDynamicPortConfig(telephonyTxOutDevice.id, telephonyTxOutDevice.id, 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_TELEPHONY_TX, 0)));
|
||||
|
||||
AudioPort telephonyRxInDevice =
|
||||
createPort(c.nextPortId++, "Telephony Rx", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0));
|
||||
c.ports.push_back(telephonyRxInDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id,
|
||||
PcmType::INT_16_BIT, AudioChannelLayout::LAYOUT_MONO, 48000, 0,
|
||||
true, createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)));
|
||||
createDynamicPortConfig(telephonyRxInDevice.id, telephonyRxInDevice.id, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_TELEPHONY_RX, 0)));
|
||||
|
||||
AudioPort fmTunerInDevice = createPort(c.nextPortId++, "FM Tuner", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0));
|
||||
c.ports.push_back(fmTunerInDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, PcmType::INT_16_BIT,
|
||||
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
|
||||
createDynamicPortConfig(fmTunerInDevice.id, fmTunerInDevice.id, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
|
||||
|
||||
// Mix ports
|
||||
|
||||
|
@ -287,13 +287,15 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
|||
|
||||
// Note: When transitioning to loading of XML configs, either keep the configuration
|
||||
// of the remote submix sources from this static configuration, or update the XML
|
||||
// config to match it. There are two reasons for that:
|
||||
// 1. The canonical r_submix configuration only lists 'STEREO' and '48000',
|
||||
// config to match it. There are several reasons for that:
|
||||
// 1. The "Remote Submix In" device is listed in the XML config as "attached",
|
||||
// however in the AIDL scheme its device type has a "virtual" connection.
|
||||
// 2. The canonical r_submix configuration only lists 'STEREO' and '48000',
|
||||
// however the framework attempts to open streams for other sample rates
|
||||
// as well. The legacy r_submix implementation allowed that, but libaudiohal@aidl
|
||||
// will not find a mix port to use. Because of that, list all channel
|
||||
// masks and sample rates that the legacy implementation allowed.
|
||||
// 2. The legacy implementation had a hard limit on the number of routes (10),
|
||||
// 3. The legacy implementation had a hard limit on the number of routes (10),
|
||||
// and this is checked indirectly by AudioPlaybackCaptureTest#testPlaybackCaptureDoS
|
||||
// CTS test. Instead of hardcoding the number of routes, we can use
|
||||
// "maxOpen/ActiveStreamCount" to enforce a similar limit. However, the canonical
|
||||
|
@ -331,15 +333,15 @@ std::unique_ptr<Configuration> getRSubmixConfiguration() {
|
|||
createPort(c.nextPortId++, "Remote Submix Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_SUBMIX, 0,
|
||||
AudioDeviceDescription::CONNECTION_VIRTUAL));
|
||||
rsubmixOutDevice.profiles = standardPcmAudioProfiles;
|
||||
c.ports.push_back(rsubmixOutDevice);
|
||||
c.connectedProfiles[rsubmixOutDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort rsubmixInDevice =
|
||||
createPort(c.nextPortId++, "Remote Submix In", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_SUBMIX, 0,
|
||||
AudioDeviceDescription::CONNECTION_VIRTUAL));
|
||||
rsubmixInDevice.profiles = standardPcmAudioProfiles;
|
||||
c.ports.push_back(rsubmixInDevice);
|
||||
c.connectedProfiles[rsubmixInDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
// Mix ports
|
||||
|
||||
|
@ -384,7 +386,7 @@ std::unique_ptr<Configuration> getRSubmixConfiguration() {
|
|||
// * "usb_device output" -> "USB Headset Out"
|
||||
// * "USB Device In", "USB Headset In" -> "usb_device input"
|
||||
//
|
||||
// Profiles for device port connected state:
|
||||
// Profiles for device port connected state (when simulating connections):
|
||||
// * "USB Device Out", "USB Headset Out":
|
||||
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
|
@ -461,9 +463,9 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
|||
// * "Test In", IN_AFE_PROXY
|
||||
// - no profiles specified
|
||||
// * "Wired Headset", OUT_HEADSET
|
||||
// - profile PCM 24-bit; STEREO; 48000
|
||||
// - no profiles specified
|
||||
// * "Wired Headset Mic", IN_HEADSET
|
||||
// - profile PCM 24-bit; MONO; 48000
|
||||
// - no profiles specified
|
||||
//
|
||||
// Mix ports:
|
||||
// * "test output", 1 max open, 1 max active stream
|
||||
|
@ -486,6 +488,10 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
|||
// * "Test Out" device port: PCM 24-bit; STEREO; 48000
|
||||
// * "Test In" device port: PCM 24-bit; MONO; 48000
|
||||
//
|
||||
// Profiles for device port connected state (when simulating connections):
|
||||
// * "Wired Headset": dynamic profiles
|
||||
// * "Wired Headset Mic": dynamic profiles
|
||||
//
|
||||
std::unique_ptr<Configuration> getStubConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
Configuration c;
|
||||
|
@ -504,8 +510,6 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
|||
createPort(c.nextPortId++, "Wired Headset", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
|
||||
AudioDeviceDescription::CONNECTION_ANALOG));
|
||||
headsetOutDevice.profiles.push_back(
|
||||
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000}));
|
||||
c.ports.push_back(headsetOutDevice);
|
||||
|
||||
AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
|
||||
|
@ -520,8 +524,6 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
|||
createPort(c.nextPortId++, "Wired Headset Mic", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
|
||||
AudioDeviceDescription::CONNECTION_ANALOG));
|
||||
headsetInDevice.profiles.push_back(
|
||||
createProfile(PcmType::INT_24_BIT, {AudioChannelLayout::LAYOUT_MONO}, {48000}));
|
||||
c.ports.push_back(headsetInDevice);
|
||||
|
||||
// Mix ports
|
||||
|
@ -553,24 +555,24 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
|||
{44100, 48000}));
|
||||
c.ports.push_back(compressedOffloadOutMix);
|
||||
|
||||
AudioPort testInMIx =
|
||||
AudioPort testInMix =
|
||||
createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
|
||||
testInMIx.profiles.push_back(
|
||||
testInMix.profiles.push_back(
|
||||
createProfile(PcmType::INT_16_BIT,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
|
||||
AudioChannelLayout::LAYOUT_FRONT_BACK},
|
||||
{8000, 11025, 16000, 22050, 32000, 44100, 48000}));
|
||||
testInMIx.profiles.push_back(
|
||||
testInMix.profiles.push_back(
|
||||
createProfile(PcmType::INT_24_BIT,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
|
||||
AudioChannelLayout::LAYOUT_FRONT_BACK},
|
||||
{8000, 11025, 16000, 22050, 32000, 44100, 48000}));
|
||||
c.ports.push_back(testInMIx);
|
||||
c.ports.push_back(testInMix);
|
||||
|
||||
c.routes.push_back(
|
||||
createRoute({testOutMix, testFastOutMix, compressedOffloadOutMix}, testOutDevice));
|
||||
c.routes.push_back(createRoute({testOutMix}, headsetOutDevice));
|
||||
c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMIx));
|
||||
c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMix));
|
||||
|
||||
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
|
||||
|
||||
|
@ -603,11 +605,19 @@ std::unique_ptr<Configuration> getStubConfiguration() {
|
|||
// "a2dp output" -> "BT A2DP Speaker"
|
||||
// "hearing aid output" -> "BT Hearing Aid Out"
|
||||
//
|
||||
// Profiles for device port connected state (when simulating connections):
|
||||
// * "BT A2DP Out", "BT A2DP Headphones", "BT A2DP Speaker":
|
||||
// - profile PCM 16-bit; STEREO; 44100, 48000, 88200, 96000
|
||||
// * "BT Hearing Aid Out":
|
||||
// - profile PCM 16-bit; STEREO; 16000, 24000
|
||||
//
|
||||
std::unique_ptr<Configuration> getBluetoothConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
const std::vector<AudioProfile> standardPcmAudioProfiles = {
|
||||
createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO},
|
||||
{44100, 48000, 88200, 96000})};
|
||||
const std::vector<AudioProfile> hearingAidAudioProfiles = {createProfile(
|
||||
PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000})};
|
||||
Configuration c;
|
||||
|
||||
// Device ports
|
||||
|
@ -645,8 +655,7 @@ std::unique_ptr<Configuration> getBluetoothConfiguration() {
|
|||
createDeviceExt(AudioDeviceType::OUT_HEARING_AID, 0,
|
||||
AudioDeviceDescription::CONNECTION_WIRELESS));
|
||||
c.ports.push_back(btOutHearingAid);
|
||||
c.connectedProfiles[btOutHearingAid.id] = std::vector<AudioProfile>(
|
||||
{createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000})});
|
||||
c.connectedProfiles[btOutHearingAid.id] = hearingAidAudioProfiles;
|
||||
|
||||
// Mix ports
|
||||
AudioPort btOutMix =
|
||||
|
@ -655,8 +664,7 @@ std::unique_ptr<Configuration> getBluetoothConfiguration() {
|
|||
|
||||
AudioPort btHearingOutMix =
|
||||
createPort(c.nextPortId++, "hearing aid output", 0, false, createPortMixExt(1, 1));
|
||||
btHearingOutMix.profiles.push_back(createProfile(
|
||||
PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {16000, 24000}));
|
||||
btHearingOutMix.profiles = hearingAidAudioProfiles;
|
||||
c.ports.push_back(btHearingOutMix);
|
||||
|
||||
c.routes.push_back(createRoute({btOutMix}, btOutDevice));
|
||||
|
|
|
@ -65,32 +65,56 @@ namespace aidl::android::hardware::audio::core {
|
|||
|
||||
namespace {
|
||||
|
||||
inline bool hasDynamicChannelMasks(const std::vector<AudioChannelLayout>& channelMasks) {
|
||||
return channelMasks.empty() ||
|
||||
std::all_of(channelMasks.begin(), channelMasks.end(),
|
||||
[](const auto& channelMask) { return channelMask == AudioChannelLayout{}; });
|
||||
}
|
||||
|
||||
inline bool hasDynamicFormat(const AudioFormatDescription& format) {
|
||||
return format == AudioFormatDescription{};
|
||||
}
|
||||
|
||||
inline bool hasDynamicSampleRates(const std::vector<int32_t>& sampleRates) {
|
||||
return sampleRates.empty() ||
|
||||
std::all_of(sampleRates.begin(), sampleRates.end(),
|
||||
[](const auto& sampleRate) { return sampleRate == 0; });
|
||||
}
|
||||
|
||||
inline bool isDynamicProfile(const AudioProfile& profile) {
|
||||
return hasDynamicFormat(profile.format) || hasDynamicChannelMasks(profile.channelMasks) ||
|
||||
hasDynamicSampleRates(profile.sampleRates);
|
||||
}
|
||||
|
||||
bool hasDynamicProfilesOnly(const std::vector<AudioProfile>& profiles) {
|
||||
if (profiles.empty()) return true;
|
||||
return std::all_of(profiles.begin(), profiles.end(), isDynamicProfile);
|
||||
}
|
||||
|
||||
// Note: does not assign an ID to the config.
|
||||
bool generateDefaultPortConfig(const AudioPort& port, AudioPortConfig* config) {
|
||||
const bool allowDynamicConfig = port.ext.getTag() == AudioPortExt::device;
|
||||
*config = {};
|
||||
config->portId = port.id;
|
||||
if (port.profiles.empty()) {
|
||||
LOG(ERROR) << __func__ << ": port " << port.id << " has no profiles";
|
||||
return false;
|
||||
for (const auto& profile : port.profiles) {
|
||||
if (isDynamicProfile(profile)) continue;
|
||||
config->format = profile.format;
|
||||
config->channelMask = *profile.channelMasks.begin();
|
||||
config->sampleRate = Int{.value = *profile.sampleRates.begin()};
|
||||
config->flags = port.flags;
|
||||
config->ext = port.ext;
|
||||
return true;
|
||||
}
|
||||
const auto& profile = port.profiles.begin();
|
||||
config->format = profile->format;
|
||||
if (profile->channelMasks.empty()) {
|
||||
LOG(ERROR) << __func__ << ": the first profile in port " << port.id
|
||||
<< " has no channel masks";
|
||||
return false;
|
||||
if (allowDynamicConfig) {
|
||||
config->format = AudioFormatDescription{};
|
||||
config->channelMask = AudioChannelLayout{};
|
||||
config->sampleRate = Int{.value = 0};
|
||||
config->flags = port.flags;
|
||||
config->ext = port.ext;
|
||||
return true;
|
||||
}
|
||||
config->channelMask = *profile->channelMasks.begin();
|
||||
if (profile->sampleRates.empty()) {
|
||||
LOG(ERROR) << __func__ << ": the first profile in port " << port.id
|
||||
<< " has no sample rates";
|
||||
return false;
|
||||
}
|
||||
Int sampleRate;
|
||||
sampleRate.value = *profile->sampleRates.begin();
|
||||
config->sampleRate = sampleRate;
|
||||
config->flags = port.flags;
|
||||
config->ext = port.ext;
|
||||
return true;
|
||||
LOG(ERROR) << __func__ << ": port " << port.id << " only has dynamic profiles";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& format,
|
||||
|
@ -314,6 +338,18 @@ std::unique_ptr<internal::Configuration> Module::initializeConfig() {
|
|||
return config;
|
||||
}
|
||||
|
||||
std::vector<AudioRoute*> Module::getAudioRoutesForAudioPortImpl(int32_t portId) {
|
||||
std::vector<AudioRoute*> result;
|
||||
auto& routes = getConfig().routes;
|
||||
for (auto& r : routes) {
|
||||
const auto& srcs = r.sourcePortIds;
|
||||
if (r.sinkPortId == portId || std::find(srcs.begin(), srcs.end(), portId) != srcs.end()) {
|
||||
result.push_back(&r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal::Configuration& Module::getConfig() {
|
||||
if (!mConfig) {
|
||||
mConfig = std::move(initializeConfig());
|
||||
|
@ -321,6 +357,24 @@ internal::Configuration& Module::getConfig() {
|
|||
return *mConfig;
|
||||
}
|
||||
|
||||
std::set<int32_t> Module::getRoutableAudioPortIds(int32_t portId,
|
||||
std::vector<AudioRoute*>* routes) {
|
||||
std::vector<AudioRoute*> routesStorage;
|
||||
if (routes == nullptr) {
|
||||
routesStorage = getAudioRoutesForAudioPortImpl(portId);
|
||||
routes = &routesStorage;
|
||||
}
|
||||
std::set<int32_t> result;
|
||||
for (AudioRoute* r : *routes) {
|
||||
if (r->sinkPortId == portId) {
|
||||
result.insert(r->sourcePortIds.begin(), r->sourcePortIds.end());
|
||||
} else {
|
||||
result.insert(r->sinkPortId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Module::registerPatch(const AudioPatch& patch) {
|
||||
auto& configs = getConfig().portConfigs;
|
||||
auto do_insert = [&](const std::vector<int32_t>& portConfigIds) {
|
||||
|
@ -510,7 +564,31 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
}
|
||||
}
|
||||
|
||||
if (connectedPort.profiles.empty()) {
|
||||
// Two main cases are considered with regard to the profiles of the connected device port:
|
||||
//
|
||||
// 1. If the template device port has dynamic profiles, and at least one routable mix
|
||||
// port also has dynamic profiles, it means that after connecting the device, the
|
||||
// connected device port must have profiles populated with actual capabilities of
|
||||
// the connected device, and dynamic of routable mix ports will be filled
|
||||
// according to these capabilities. An example of this case is connection of an
|
||||
// HDMI or USB device. For USB handled by ADSP, there can be mix ports with static
|
||||
// profiles, and one dedicated mix port for "hi-fi" playback. The latter is left with
|
||||
// dynamic profiles so that they can be populated with actual capabilities of
|
||||
// the connected device.
|
||||
//
|
||||
// 2. If the template device port has dynamic profiles, while all routable mix ports
|
||||
// have static profiles, it means that after connecting the device, the connected
|
||||
// device port can be left with dynamic profiles, and profiles of mix ports are
|
||||
// left untouched. An example of this case is connection of an analog wired
|
||||
// headset, it should be treated in the same way as a speaker.
|
||||
//
|
||||
// Yet another possible case is when both the template device port and all routable
|
||||
// mix ports have static profiles. This is allowed and handled correctly, however, it
|
||||
// is not very practical, since these profiles are likely duplicates of each other.
|
||||
|
||||
std::vector<AudioRoute*> routesToMixPorts = getAudioRoutesForAudioPortImpl(templateId);
|
||||
std::set<int32_t> routableMixPortIds = getRoutableAudioPortIds(templateId, &routesToMixPorts);
|
||||
if (hasDynamicProfilesOnly(connectedPort.profiles)) {
|
||||
if (!mDebug.simulateDeviceConnections) {
|
||||
RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort));
|
||||
} else {
|
||||
|
@ -520,23 +598,22 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
connectedPort.profiles = connectedProfilesIt->second;
|
||||
}
|
||||
}
|
||||
if (connectedPort.profiles.empty()) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": profiles of a connected port still empty after connecting external "
|
||||
"device "
|
||||
<< connectedPort.toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto profile : connectedPort.profiles) {
|
||||
if (profile.channelMasks.empty()) {
|
||||
LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no channel masks";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
if (profile.sampleRates.empty()) {
|
||||
LOG(ERROR) << __func__ << ": the profile " << profile.name << " has no sample rates";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
if (hasDynamicProfilesOnly(connectedPort.profiles)) {
|
||||
// Possible case 2. Check if all routable mix ports have static profiles.
|
||||
if (auto dynamicMixPortIt = std::find_if(ports.begin(), ports.end(),
|
||||
[&routableMixPortIds](const auto& p) {
|
||||
return routableMixPortIds.count(p.id) >
|
||||
0 &&
|
||||
hasDynamicProfilesOnly(p.profiles);
|
||||
});
|
||||
dynamicMixPortIt != ports.end()) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": connected port only has dynamic profiles after connecting "
|
||||
<< "external device " << connectedPort.toString() << ", and there exist "
|
||||
<< "a routable mix port with dynamic profiles: "
|
||||
<< dynamicMixPortIt->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -548,44 +625,36 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
ports.push_back(connectedPort);
|
||||
onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
|
||||
|
||||
std::vector<int32_t> routablePortIds;
|
||||
// For routes where the template port is a source, add the connected port to sources,
|
||||
// otherwise, create a new route by copying from the route for the template port.
|
||||
std::vector<AudioRoute> newRoutes;
|
||||
auto& routes = getConfig().routes;
|
||||
for (auto& r : routes) {
|
||||
if (r.sinkPortId == templateId) {
|
||||
AudioRoute newRoute;
|
||||
newRoute.sourcePortIds = r.sourcePortIds;
|
||||
newRoute.sinkPortId = connectedPort.id;
|
||||
newRoute.isExclusive = r.isExclusive;
|
||||
newRoutes.push_back(std::move(newRoute));
|
||||
routablePortIds.insert(routablePortIds.end(), r.sourcePortIds.begin(),
|
||||
r.sourcePortIds.end());
|
||||
for (AudioRoute* r : routesToMixPorts) {
|
||||
if (r->sinkPortId == templateId) {
|
||||
newRoutes.push_back(AudioRoute{.sourcePortIds = r->sourcePortIds,
|
||||
.sinkPortId = connectedPort.id,
|
||||
.isExclusive = r->isExclusive});
|
||||
} else {
|
||||
auto& srcs = r.sourcePortIds;
|
||||
if (std::find(srcs.begin(), srcs.end(), templateId) != srcs.end()) {
|
||||
srcs.push_back(connectedPort.id);
|
||||
routablePortIds.push_back(r.sinkPortId);
|
||||
}
|
||||
r->sourcePortIds.push_back(connectedPort.id);
|
||||
}
|
||||
}
|
||||
auto& routes = getConfig().routes;
|
||||
routes.insert(routes.end(), newRoutes.begin(), newRoutes.end());
|
||||
|
||||
// Note: this is a simplistic approach assuming that a mix port can only be populated
|
||||
// from a single device port. Implementing support for stuffing dynamic profiles with a superset
|
||||
// of all profiles from all routable dynamic device ports would be more involved.
|
||||
for (const auto mixPortId : routablePortIds) {
|
||||
auto portsIt = findById<AudioPort>(ports, mixPortId);
|
||||
if (portsIt != ports.end()) {
|
||||
if (portsIt->profiles.empty()) {
|
||||
portsIt->profiles = connectedPort.profiles;
|
||||
connectedPortsIt->second.insert(portsIt->id);
|
||||
if (!hasDynamicProfilesOnly(connectedPort.profiles) && !routableMixPortIds.empty()) {
|
||||
// Note: this is a simplistic approach assuming that a mix port can only be populated
|
||||
// from a single device port. Implementing support for stuffing dynamic profiles with
|
||||
// a superset of all profiles from all routable dynamic device ports would be more involved.
|
||||
for (auto& port : ports) {
|
||||
if (routableMixPortIds.count(port.id) == 0) continue;
|
||||
if (hasDynamicProfilesOnly(port.profiles)) {
|
||||
port.profiles = connectedPort.profiles;
|
||||
connectedPortsIt->second.insert(port.id);
|
||||
} else {
|
||||
// Check if profiles are non empty because they were populated by
|
||||
// a previous connection. Otherwise, it means that they are not empty because
|
||||
// the mix port has static profiles.
|
||||
for (const auto cp : mConnectedDevicePorts) {
|
||||
if (cp.second.count(portsIt->id) > 0) {
|
||||
connectedPortsIt->second.insert(portsIt->id);
|
||||
// Check if profiles are not all dynamic because they were populated by
|
||||
// a previous connection. Otherwise, it means that they are actually static.
|
||||
for (const auto& cp : mConnectedDevicePorts) {
|
||||
if (cp.second.count(port.id) > 0) {
|
||||
connectedPortsIt->second.insert(port.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -705,13 +774,9 @@ ndk::ScopedAStatus Module::getAudioRoutesForAudioPort(int32_t in_portId,
|
|||
LOG(ERROR) << __func__ << ": port id " << in_portId << " not found";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
auto& routes = getConfig().routes;
|
||||
std::copy_if(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
|
||||
[&](const auto& r) {
|
||||
const auto& srcs = r.sourcePortIds;
|
||||
return r.sinkPortId == in_portId ||
|
||||
std::find(srcs.begin(), srcs.end(), in_portId) != srcs.end();
|
||||
});
|
||||
std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(in_portId);
|
||||
std::transform(routes.begin(), routes.end(), std::back_inserter(*_aidl_return),
|
||||
[](auto rptr) { return *rptr; });
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
|
@ -925,13 +990,14 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste
|
|||
|
||||
const int portId = existing != configs.end() ? existing->portId : in_requested.portId;
|
||||
if (portId == 0) {
|
||||
LOG(ERROR) << __func__ << ": input port config does not specify portId";
|
||||
LOG(ERROR) << __func__ << ": requested port config does not specify portId";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
auto& ports = getConfig().ports;
|
||||
auto portIt = findById<AudioPort>(ports, portId);
|
||||
if (portIt == ports.end()) {
|
||||
LOG(ERROR) << __func__ << ": input port config points to non-existent portId " << portId;
|
||||
LOG(ERROR) << __func__ << ": requested port config points to non-existent portId "
|
||||
<< portId;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
if (existing != configs.end()) {
|
||||
|
@ -949,6 +1015,10 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste
|
|||
// or a new generated config. Now attempt to update it according to the specified
|
||||
// fields of 'in_requested'.
|
||||
|
||||
// Device ports with only dynamic profiles are used for devices that are connected via ADSP,
|
||||
// which takes care of their actual configuration automatically.
|
||||
const bool allowDynamicConfig = portIt->ext.getTag() == AudioPortExt::device &&
|
||||
hasDynamicProfilesOnly(portIt->profiles);
|
||||
bool requestedIsValid = true, requestedIsFullySpecified = true;
|
||||
|
||||
AudioIoFlags portFlags = portIt->flags;
|
||||
|
@ -966,17 +1036,19 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste
|
|||
AudioProfile portProfile;
|
||||
if (in_requested.format.has_value()) {
|
||||
const auto& format = in_requested.format.value();
|
||||
if (findAudioProfile(*portIt, format, &portProfile)) {
|
||||
if ((format == AudioFormatDescription{} && allowDynamicConfig) ||
|
||||
findAudioProfile(*portIt, format, &portProfile)) {
|
||||
out_suggested->format = format;
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << ": requested format " << format.toString()
|
||||
<< " is not found in port's " << portId << " profiles";
|
||||
<< " is not found in the profiles of port " << portId;
|
||||
requestedIsValid = false;
|
||||
}
|
||||
} else {
|
||||
requestedIsFullySpecified = false;
|
||||
}
|
||||
if (!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
|
||||
if (!(out_suggested->format.value() == AudioFormatDescription{} && allowDynamicConfig) &&
|
||||
!findAudioProfile(*portIt, out_suggested->format.value(), &portProfile)) {
|
||||
LOG(ERROR) << __func__ << ": port " << portId << " does not support format "
|
||||
<< out_suggested->format.value().toString() << " anymore";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
|
@ -984,8 +1056,9 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste
|
|||
|
||||
if (in_requested.channelMask.has_value()) {
|
||||
const auto& channelMask = in_requested.channelMask.value();
|
||||
if (find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
|
||||
portProfile.channelMasks.end()) {
|
||||
if ((channelMask == AudioChannelLayout{} && allowDynamicConfig) ||
|
||||
find(portProfile.channelMasks.begin(), portProfile.channelMasks.end(), channelMask) !=
|
||||
portProfile.channelMasks.end()) {
|
||||
out_suggested->channelMask = channelMask;
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << ": requested channel mask " << channelMask.toString()
|
||||
|
@ -999,7 +1072,8 @@ ndk::ScopedAStatus Module::setAudioPortConfig(const AudioPortConfig& in_requeste
|
|||
|
||||
if (in_requested.sampleRate.has_value()) {
|
||||
const auto& sampleRate = in_requested.sampleRate.value();
|
||||
if (find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
|
||||
if ((sampleRate.value == 0 && allowDynamicConfig) ||
|
||||
find(portProfile.sampleRates.begin(), portProfile.sampleRates.end(),
|
||||
sampleRate.value) != portProfile.sampleRates.end()) {
|
||||
out_suggested->sampleRate = sampleRate;
|
||||
} else {
|
||||
|
@ -1397,7 +1471,18 @@ bool Module::isMmapSupported() {
|
|||
return mIsMmapSupported.value();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
|
||||
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort) {
|
||||
if (audioPort->ext.getTag() != AudioPortExt::device) {
|
||||
LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
|
||||
if (!devicePort.device.type.connection.empty()) {
|
||||
LOG(ERROR) << __func__
|
||||
<< ": module implementation must override 'populateConnectedDevicePort' "
|
||||
<< "to handle connection of external devices.";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
|
|
@ -18,15 +18,23 @@
|
|||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "BluetoothAudioSessionControl.h"
|
||||
#include "core-impl/ModuleBluetooth.h"
|
||||
#include "core-impl/StreamBluetooth.h"
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
using aidl::android::media::audio::common::MicrophoneInfo;
|
||||
using ::aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using ::aidl::android::hardware::audio::common::SourceMetadata;
|
||||
using ::aidl::android::hardware::bluetooth::audio::BluetoothAudioSession;
|
||||
using ::aidl::android::media::audio::common::AudioDeviceDescription;
|
||||
using ::aidl::android::media::audio::common::AudioDeviceType;
|
||||
using ::aidl::android::media::audio::common::AudioOffloadInfo;
|
||||
using ::aidl::android::media::audio::common::AudioPort;
|
||||
using ::aidl::android::media::audio::common::AudioPortExt;
|
||||
using ::aidl::android::media::audio::common::MicrophoneInfo;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidl;
|
||||
using ::android::bluetooth::audio::aidl::BluetoothAudioPortAidlOut;
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::getBluetoothA2dp(
|
||||
std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
|
||||
|
@ -80,6 +88,49 @@ ndk::ScopedAStatus ModuleBluetooth::createOutputStream(
|
|||
offloadInfo, getBtProfileManagerHandles());
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::populateConnectedDevicePort(AudioPort* audioPort) {
|
||||
if (audioPort->ext.getTag() != AudioPortExt::device) {
|
||||
LOG(ERROR) << __func__ << ": not a device port: " << audioPort->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& devicePort = audioPort->ext.get<AudioPortExt::device>();
|
||||
const auto& description = devicePort.device.type;
|
||||
// Since the configuration of the BT module is static, there is nothing to populate here.
|
||||
// However, this method must return an error when the device can not be connected,
|
||||
// this is determined by the status of BT profiles.
|
||||
if (description.connection == AudioDeviceDescription::CONNECTION_BT_A2DP) {
|
||||
bool isA2dpEnabled = false;
|
||||
if (!!mBluetoothA2dp) {
|
||||
RETURN_STATUS_IF_ERROR(mBluetoothA2dp.getInstance()->isEnabled(&isA2dpEnabled));
|
||||
}
|
||||
return isA2dpEnabled ? ndk::ScopedAStatus::ok()
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_BT_LE) {
|
||||
bool isLeEnabled = false;
|
||||
if (!!mBluetoothLe) {
|
||||
RETURN_STATUS_IF_ERROR(mBluetoothLe.getInstance()->isEnabled(&isLeEnabled));
|
||||
}
|
||||
return isLeEnabled ? ndk::ScopedAStatus::ok()
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
} else if (description.connection == AudioDeviceDescription::CONNECTION_WIRELESS &&
|
||||
description.type == AudioDeviceType::OUT_HEARING_AID) {
|
||||
// Hearing aids can use a number of profiles, thus the only way to check
|
||||
// connectivity is to try to talk to the BT HAL.
|
||||
if (!BluetoothAudioSession::IsAidlAvailable()) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
std::shared_ptr<BluetoothAudioPortAidl> proxy = std::shared_ptr<BluetoothAudioPortAidl>(
|
||||
std::make_shared<BluetoothAudioPortAidlOut>());
|
||||
if (proxy->registerPort(description)) {
|
||||
proxy->unregisterPort();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
LOG(ERROR) << __func__ << ": unsupported device type: " << audioPort->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleBluetooth::onMasterMuteChanged(bool) {
|
||||
LOG(DEBUG) << __func__ << ": is not supported";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
|
|
|
@ -202,6 +202,7 @@ class Module : public BnModule {
|
|||
std::set<int32_t> findConnectedPortConfigIds(int32_t portConfigId);
|
||||
ndk::ScopedAStatus findPortIdForNewStream(
|
||||
int32_t in_portConfigId, ::aidl::android::media::audio::common::AudioPort** port);
|
||||
std::vector<AudioRoute*> getAudioRoutesForAudioPortImpl(int32_t portId);
|
||||
virtual BtProfileHandles getBtProfileManagerHandles();
|
||||
internal::Configuration& getConfig();
|
||||
const ConnectedDevicePorts& getConnectedDevicePorts() const { return mConnectedDevicePorts; }
|
||||
|
@ -209,6 +210,8 @@ class Module : public BnModule {
|
|||
bool getMasterVolume() const { return mMasterVolume; }
|
||||
bool getMicMute() const { return mMicMute; }
|
||||
const Patches& getPatches() const { return mPatches; }
|
||||
std::set<int32_t> getRoutableAudioPortIds(int32_t portId,
|
||||
std::vector<AudioRoute*>* routes = nullptr);
|
||||
const Streams& getStreams() const { return mStreams; }
|
||||
Type getType() const { return mType; }
|
||||
bool isMmapSupported();
|
||||
|
|
|
@ -44,6 +44,8 @@ class ModuleBluetooth final : public Module {
|
|||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) override;
|
||||
ndk::ScopedAStatus populateConnectedDevicePort(
|
||||
::aidl::android::media::audio::common::AudioPort* audioPort) override;
|
||||
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
|
||||
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
|
||||
|
||||
|
@ -51,4 +53,4 @@ class ModuleBluetooth final : public Module {
|
|||
ChildInterface<IBluetoothLe> mBluetoothLe;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
|
|
@ -59,23 +59,22 @@ ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream(
|
|||
|
||||
ndk::ScopedAStatus ModuleRemoteSubmix::populateConnectedDevicePort(AudioPort* audioPort) {
|
||||
// Find the corresponding mix port and copy its profiles.
|
||||
std::vector<AudioRoute> routes;
|
||||
// At this moment, the port has the same ID as the template port, see connectExternalDevice.
|
||||
RETURN_STATUS_IF_ERROR(getAudioRoutesForAudioPort(audioPort->id, &routes));
|
||||
std::vector<AudioRoute*> routes = getAudioRoutesForAudioPortImpl(audioPort->id);
|
||||
if (routes.empty()) {
|
||||
LOG(ERROR) << __func__ << ": no routes found for the port " << audioPort->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
const auto& route = *routes.begin();
|
||||
AudioPort mixPort;
|
||||
if (route.sinkPortId == audioPort->id) {
|
||||
if (route.sourcePortIds.empty()) {
|
||||
LOG(ERROR) << __func__ << ": invalid route " << route.toString();
|
||||
if (route->sinkPortId == audioPort->id) {
|
||||
if (route->sourcePortIds.empty()) {
|
||||
LOG(ERROR) << __func__ << ": invalid route " << route->toString();
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
RETURN_STATUS_IF_ERROR(getAudioPort(*route.sourcePortIds.begin(), &mixPort));
|
||||
RETURN_STATUS_IF_ERROR(getAudioPort(*route->sourcePortIds.begin(), &mixPort));
|
||||
} else {
|
||||
RETURN_STATUS_IF_ERROR(getAudioPort(route.sinkPortId, &mixPort));
|
||||
RETURN_STATUS_IF_ERROR(getAudioPort(route->sinkPortId, &mixPort));
|
||||
}
|
||||
audioPort->profiles = mixPort.profiles;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
|
|
|
@ -281,6 +281,22 @@ std::optional<AudioPort> ModuleConfig::getSourceMixPortForConnectedDevice() cons
|
|||
return {};
|
||||
}
|
||||
|
||||
std::vector<AudioPort> ModuleConfig::getRoutableMixPortsForDevicePort(const AudioPort& port) const {
|
||||
std::set<int32_t> portIds;
|
||||
for (const auto& route : mRoutes) {
|
||||
if (port.id == route.sinkPortId) {
|
||||
portIds.insert(route.sourcePortIds.begin(), route.sourcePortIds.end());
|
||||
} else if (auto it = std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(),
|
||||
port.id);
|
||||
it != route.sourcePortIds.end()) {
|
||||
portIds.insert(route.sinkPortId);
|
||||
}
|
||||
}
|
||||
const bool isInput = port.flags.getTag() == AudioIoFlags::input;
|
||||
return findMixPorts(isInput, false /*connectedOnly*/, false /*singlePort*/,
|
||||
[&portIds](const AudioPort& p) { return portIds.count(p.id) > 0; });
|
||||
}
|
||||
|
||||
std::optional<ModuleConfig::SrcSinkPair> ModuleConfig::getNonRoutableSrcSinkPair(
|
||||
bool isInput) const {
|
||||
const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/);
|
||||
|
|
|
@ -103,6 +103,9 @@ class ModuleConfig {
|
|||
std::optional<aidl::android::media::audio::common::AudioPort>
|
||||
getSourceMixPortForConnectedDevice() const;
|
||||
|
||||
std::vector<aidl::android::media::audio::common::AudioPort> getRoutableMixPortsForDevicePort(
|
||||
const aidl::android::media::audio::common::AudioPort& port) const;
|
||||
|
||||
std::optional<SrcSinkPair> getNonRoutableSrcSinkPair(bool isInput) const;
|
||||
std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
|
||||
std::vector<SrcSinkGroup> getRoutableSrcSinkGroups(bool isInput) const;
|
||||
|
|
|
@ -1501,8 +1501,25 @@ TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) {
|
|||
<< "port ID " << connectedPortId;
|
||||
EXPECT_EQ(portConnected.get(), connectedPort);
|
||||
const auto& portProfiles = connectedPort.profiles;
|
||||
EXPECT_NE(0UL, portProfiles.size())
|
||||
<< "Connected port has no profiles: " << connectedPort.toString();
|
||||
if (portProfiles.empty()) {
|
||||
const auto routableMixPorts =
|
||||
moduleConfig->getRoutableMixPortsForDevicePort(connectedPort);
|
||||
bool hasMixPortWithStaticProfile = false;
|
||||
for (const auto& mixPort : routableMixPorts) {
|
||||
const auto& mixPortProfiles = mixPort.profiles;
|
||||
if (!mixPortProfiles.empty() &&
|
||||
!std::all_of(mixPortProfiles.begin(), mixPortProfiles.end(),
|
||||
[](const auto& profile) {
|
||||
return profile.format.type == AudioFormatType::DEFAULT;
|
||||
})) {
|
||||
hasMixPortWithStaticProfile = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(hasMixPortWithStaticProfile)
|
||||
<< "Connected port has no profiles and no routable mix ports with profiles: "
|
||||
<< connectedPort.toString();
|
||||
}
|
||||
const auto dynamicProfileIt =
|
||||
std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) {
|
||||
return profile.format.type == AudioFormatType::DEFAULT;
|
||||
|
@ -1586,7 +1603,8 @@ TEST_P(AudioCoreModule, ResetAudioPortConfigToInitialValue) {
|
|||
EXPECT_NE(portConfigsAfter.end(), afterIt)
|
||||
<< " port config ID " << c.id << " was removed by reset";
|
||||
if (afterIt != portConfigsAfter.end()) {
|
||||
EXPECT_EQ(c, *afterIt);
|
||||
EXPECT_TRUE(c == *afterIt)
|
||||
<< "Expected: " << c.toString() << "; Actual: " << afterIt->toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue