From e59a6ce2e0c114407cb417d1ef1f9437b6b09570 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 23 Aug 2023 14:11:42 -0700 Subject: [PATCH 1/9] audio: Make IConfig.getSurroundSound default implementation more robust In the case when there is a problem with the legacy APM XML file, the converter provides a default surround sound config. However, the default implementation of IConfig::getSurroundSoundConfig did not take an advantage of that, and was returning an empty config, which is not accepted by VTS. Also, improve logging messages: clarify the situation when no readable audio policy XML file found, and use outer functions name for lambdas. Bug: 293978054 Test: atest VtsHalAudioCoreTargetTest (cherry picked from https://android-review.googlesource.com/q/commit:1e25ef808f5e6a29d2a889213f4b42cf58ecdc29) Merged-In: Iae069a0498009605ef5ededb9c9112efab08548a Change-Id: Iae069a0498009605ef5ededb9c9112efab08548a --- audio/aidl/default/Config.cpp | 18 +++++++++--------- .../default/include/core-impl/XmlConverter.h | 12 +++++++++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/audio/aidl/default/Config.cpp b/audio/aidl/default/Config.cpp index d1023da6f5..308200abf6 100644 --- a/audio/aidl/default/Config.cpp +++ b/audio/aidl/default/Config.cpp @@ -27,12 +27,11 @@ using aidl::android::media::audio::common::AudioHalEngineConfig; namespace aidl::android::hardware::audio::core { ndk::ScopedAStatus Config::getSurroundSoundConfig(SurroundSoundConfig* _aidl_return) { + static const auto& func = __func__; static const SurroundSoundConfig surroundSoundConfig = [this]() { - SurroundSoundConfig surroundCfg; - if (mAudioPolicyConverter.getStatus() == ::android::OK) { - surroundCfg = mAudioPolicyConverter.getSurroundSoundConfig(); - } else { - LOG(WARNING) << __func__ << mAudioPolicyConverter.getError(); + SurroundSoundConfig surroundCfg = mAudioPolicyConverter.getSurroundSoundConfig(); + if (mAudioPolicyConverter.getStatus() != ::android::OK) { + LOG(WARNING) << func << ": " << mAudioPolicyConverter.getError(); } return surroundCfg; }(); @@ -42,21 +41,22 @@ ndk::ScopedAStatus Config::getSurroundSoundConfig(SurroundSoundConfig* _aidl_ret } ndk::ScopedAStatus Config::getEngineConfig(AudioHalEngineConfig* _aidl_return) { + static const auto& func = __func__; static const AudioHalEngineConfig returnEngCfg = [this]() { AudioHalEngineConfig engConfig; if (mEngConfigConverter.getStatus() == ::android::OK) { engConfig = mEngConfigConverter.getAidlEngineConfig(); } else { - LOG(INFO) << __func__ << mEngConfigConverter.getError(); + LOG(INFO) << func << ": " << mEngConfigConverter.getError(); if (mAudioPolicyConverter.getStatus() == ::android::OK) { engConfig = mAudioPolicyConverter.getAidlEngineConfig(); } else { - LOG(WARNING) << __func__ << mAudioPolicyConverter.getError(); + LOG(WARNING) << func << ": " << mAudioPolicyConverter.getError(); } } // Logging full contents of the config is an overkill, just provide statistics. - LOG(DEBUG) << "getEngineConfig: number of strategies parsed: " - << engConfig.productStrategies.size() + LOG(DEBUG) << func + << ": number of strategies parsed: " << engConfig.productStrategies.size() << ", default strategy: " << engConfig.defaultProductStrategyId << ", number of volume groups parsed: " << engConfig.volumeGroups.size(); return engConfig; diff --git a/audio/aidl/default/include/core-impl/XmlConverter.h b/audio/aidl/default/include/core-impl/XmlConverter.h index a68a6fdde9..383ea24836 100644 --- a/audio/aidl/default/include/core-impl/XmlConverter.h +++ b/audio/aidl/default/include/core-impl/XmlConverter.h @@ -53,10 +53,16 @@ class XmlConverter { const ::android::status_t& status) { std::string errorMessage; if (status != ::android::OK) { - if (!isReadableConfigFile) { - errorMessage = "Could not read requested config file:" + configFilePath; + if (configFilePath.empty()) { + errorMessage = "No audio configuration files found"; + } else if (!isReadableConfigFile) { + errorMessage = std::string("Could not read requested XML config file: \"") + .append(configFilePath) + .append("\""); } else { - errorMessage = "Invalid config file: " + configFilePath; + errorMessage = std::string("Invalid XML config file: \"") + .append(configFilePath) + .append("\""); } } return errorMessage; From 2e53f30f72afaf215606a8076df09f09cb8364cc Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Fri, 18 Aug 2023 23:58:05 +0000 Subject: [PATCH 2/9] Remove the limitation of max open streams Bug: 295055755 Test: atest CtsMediaAudioTestCases (cherry picked from https://android-review.googlesource.com/q/commit:2461891a1c7cc29352e82cb6fd1f23587422aa28) Merged-In: Ifc0bec23ccc2845657389783194eb18fcc7884fa Change-Id: Ifc0bec23ccc2845657389783194eb18fcc7884fa --- audio/aidl/default/Configuration.cpp | 4 ++-- audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index 8e02e7dfa7..9131935f87 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -230,14 +230,14 @@ std::unique_ptr getPrimaryConfiguration() { AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output", makeBitPositionFlagMask(AudioOutputFlags::PRIMARY), - false, createPortMixExt(1, 1)); + false, createPortMixExt(0, 0)); primaryOutMix.profiles.insert(primaryOutMix.profiles.begin(), standardPcmAudioProfiles.begin(), standardPcmAudioProfiles.end()); c.ports.push_back(primaryOutMix); AudioPort primaryInMix = - createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(1, 1)); + createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 0)); primaryInMix.profiles.push_back( createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO}, diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp index 68e7151191..8a8998e10c 100644 --- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp @@ -1379,7 +1379,7 @@ TEST_P(AudioCoreModule, CheckMixPorts) { << "At least two mix ports have PRIMARY flag set: " << primaryMixPort.value() << " and " << port.id; primaryMixPort = port.id; - EXPECT_EQ(1, mixPort.maxOpenStreamCount) + EXPECT_GE(mixPort.maxOpenStreamCount, 0) << "Primary mix port " << port.id << " can not have maxOpenStreamCount " << mixPort.maxOpenStreamCount; } From 9a4436477fe8611975f6413f8fdb4d55a7e3ba51 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Thu, 7 Sep 2023 17:52:45 +0000 Subject: [PATCH 3/9] Remove unused file EffectWorker.h Bug: 299213582 Test: m (cherry picked from https://android-review.googlesource.com/q/commit:5b8638c66c2113d13a2aaef505fa7a60b95f3811) Merged-In: I9ffd2e4e1cf1fbca69650eb101812db2ed3acc6f Change-Id: I9ffd2e4e1cf1fbca69650eb101812db2ed3acc6f --- .../include/effect-impl/EffectWorker.h | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 audio/aidl/default/include/effect-impl/EffectWorker.h diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h deleted file mode 100644 index 421429acaf..0000000000 --- a/audio/aidl/default/include/effect-impl/EffectWorker.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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. - */ - -#pragma once -#include -#include -#include -#include - -#include "EffectContext.h" -#include "EffectThread.h" - -namespace aidl::android::hardware::audio::effect { - -std::string toString(RetCode& code); - -class EffectWorker : public EffectThread { - public: - // set effect context for worker, suppose to only happen once here - void setContext(std::shared_ptr context) { - std::call_once(mOnceFlag, [&]() { mContext = context; }); - }; - - // handle FMQ and call effect implemented virtual function - void process() override { - RETURN_VALUE_IF(!mContext, void(), "nullContext"); - std::shared_ptr statusMQ = mContext->getStatusFmq(); - std::shared_ptr inputMQ = mContext->getInputDataFmq(); - std::shared_ptr outputMQ = mContext->getOutputDataFmq(); - - // Only this worker will read from input data MQ and write to output data MQ. - auto readSamples = inputMQ->availableToRead(), writeSamples = outputMQ->availableToWrite(); - if (readSamples && writeSamples) { - auto processSamples = std::min(readSamples, writeSamples); - LOG(VERBOSE) << __func__ << " available to read " << readSamples - << " available to write " << writeSamples << " process " << processSamples; - - auto buffer = mContext->getWorkBuffer(); - inputMQ->read(buffer, processSamples); - - IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples); - outputMQ->write(buffer, status.fmqProduced); - statusMQ->writeBlocking(&status, 1); - LOG(VERBOSE) << __func__ << " done processing, effect consumed " << status.fmqConsumed - << " produced " << status.fmqProduced; - } else { - // TODO: maybe add some sleep here to avoid busy waiting - } - } - - // must implement by each effect implementation - // TODO: consider if this interface need adjustment to handle in-place processing - virtual IEffect::Status effectProcessImpl(float* in, float* out, int samples) = 0; - - private: - // make sure the context only set once. - std::once_flag mOnceFlag; - std::shared_ptr mContext; -}; - -} // namespace aidl::android::hardware::audio::effect From 2dc898026a2e3f9eb536139f10b56799da6ae98e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Thu, 7 Sep 2023 16:30:11 -0700 Subject: [PATCH 4/9] audio: Properly support external device port with static configs External device ports might not have dynamic profiles, for example, ports for BT devices. Properly handle this case in the default implementation. Bug: 264712385 Test: atest VtsHalAudioCoreTargetTest Test: atest audiosystem_tests (cherry picked from https://android-review.googlesource.com/q/commit:fcf980e586d9b0f5ad09ba9098cd2c77911526b4) Merged-In: I8d4bcbf6ccf2ba05436e68e3ba94567fc7610eb7 Change-Id: I8d4bcbf6ccf2ba05436e68e3ba94567fc7610eb7 --- audio/aidl/default/Module.cpp | 37 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index f81095e02c..b7761bf244 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -454,16 +454,15 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA LOG(ERROR) << __func__ << ": port id " << templateId << " is not a device port"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } - if (!templateIt->profiles.empty()) { - LOG(ERROR) << __func__ << ": port id " << templateId - << " does not have dynamic profiles"; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); - } auto& templateDevicePort = templateIt->ext.get(); if (templateDevicePort.device.type.connection.empty()) { LOG(ERROR) << __func__ << ": port id " << templateId << " is permanently attached"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + if (mConnectedDevicePorts.find(templateId) != mConnectedDevicePorts.end()) { + LOG(ERROR) << __func__ << ": port id " << templateId << " is a connected device port"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } // Postpone id allocation until we ensure that there are no client errors. connectedPort = *templateIt; connectedPort.extraAudioDescriptors = in_templateIdAndAdditionalData.extraAudioDescriptors; @@ -486,19 +485,23 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA } } - if (!mDebug.simulateDeviceConnections) { - RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort)); - } else { - auto& connectedProfiles = getConfig().connectedProfiles; - if (auto connectedProfilesIt = connectedProfiles.find(templateId); - connectedProfilesIt != connectedProfiles.end()) { - connectedPort.profiles = connectedProfilesIt->second; - } - } if (connectedPort.profiles.empty()) { - LOG(ERROR) << "Profiles of a connected port still empty after connecting external device " - << connectedPort.toString(); - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + if (!mDebug.simulateDeviceConnections) { + RETURN_STATUS_IF_ERROR(populateConnectedDevicePort(&connectedPort)); + } else { + auto& connectedProfiles = getConfig().connectedProfiles; + if (auto connectedProfilesIt = connectedProfiles.find(templateId); + connectedProfilesIt != connectedProfiles.end()) { + 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) { From 1a828f1f95e8ee7f2f05de4cfcde0ddc9932d0b8 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Thu, 7 Sep 2023 17:42:39 +0000 Subject: [PATCH 5/9] Avoid sub_overflow_minimal in AIDL effects VTS Bug: 299385610 Test: atest VtsHalHapticGeneratorTargetTest (cherry picked from https://android-review.googlesource.com/q/commit:5ed80c5144d6b0e9064e7c5a34343de29d6f431e) Merged-In: I1721fb87dd373a40453505733c8aaee647b4cf3b Change-Id: I1721fb87dd373a40453505733c8aaee647b4cf3b --- audio/aidl/vts/EffectHelper.h | 6 +++--- audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h index 685d07d191..2c8edf28fb 100644 --- a/audio/aidl/vts/EffectHelper.h +++ b/audio/aidl/vts/EffectHelper.h @@ -250,11 +250,11 @@ class EffectHelper { maxLimit = std::numeric_limits::max(); if (s.size()) { const auto min = *s.begin(), max = *s.rbegin(); - s.insert(min + (max - min) / 2); - if (min != minLimit) { + s.insert((min & max) + ((min ^ max) >> 1)); + if (min > minLimit + 1) { s.insert(min - 1); } - if (max != maxLimit) { + if (max < maxLimit - 1) { s.insert(max + 1); } } diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp index 54caed920e..b33234b04b 100644 --- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp +++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp @@ -208,7 +208,7 @@ INSTANTIATE_TEST_SUITE_P( HapticGeneratorInvalidTest, HapticGeneratorParamTest, ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( IFactory::descriptor, getEffectTypeUuidHapticGenerator())), - testing::Values(MIN_ID - 1), + testing::Values(MIN_ID), testing::Values(HapticGenerator::VibratorScale::NONE), testing::Values(MIN_FLOAT), testing::Values(MIN_FLOAT), testing::Values(MIN_FLOAT)), From e8ae75d310311691f668e8aac7ee92149ce3c8c7 Mon Sep 17 00:00:00 2001 From: Shraddha Basantwani Date: Tue, 22 Aug 2023 15:07:16 +0000 Subject: [PATCH 6/9] r_submix : Fix minor issues in AIDL implementation 1. Update refinePosition for output stream 2. Add missing exitStandby on transfer 3. Add check for availableToRead in read functionality Bug: 286914845 Test: atest VtsHalAudioCoreTargetTest (cherry picked from https://android-review.googlesource.com/q/commit:95f9232923139f38d802054f5df27a73f1328a09) Merged-In: Ibab914e5e09474c2b55a6c64cc004ebc1bb6cb47 Change-Id: Ibab914e5e09474c2b55a6c64cc004ebc1bb6cb47 --- .../default/r_submix/StreamRemoteSubmix.cpp | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp index 3134b86841..9c9c08b048 100644 --- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp +++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp @@ -179,7 +179,7 @@ void StreamRemoteSubmix::shutdown() { LOG(ERROR) << __func__ << ": transfer without a pipe!"; return ::android::UNEXPECTED_NULL; } - + mCurrentRoute->exitStandby(mIsInput); return (mIsInput ? inRead(buffer, frameCount, actualFrameCount) : outWrite(buffer, frameCount, actualFrameCount)); } @@ -190,17 +190,14 @@ void StreamRemoteSubmix::shutdown() { return ::android::NO_INIT; } const ssize_t framesInPipe = source->availableToRead(); - if (framesInPipe < 0) { - return ::android::INVALID_OPERATION; + if (framesInPipe <= 0) { + // No need to update the position frames + return ::android::OK; } if (mIsInput) { position->frames += framesInPipe; - } else { - if (position->frames > framesInPipe) { - position->frames -= framesInPipe; - } else { - position->frames = 0; - } + } else if (position->frames >= framesInPipe) { + position->frames -= framesInPipe; } return ::android::OK; } @@ -280,18 +277,14 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { size_t* actualFrameCount) { // about to read from audio source sp source = mCurrentRoute->getSource(); - if (source == nullptr || source->availableToRead() == 0) { - if (source == nullptr) { - int readErrorCount = mCurrentRoute->notifyReadError(); - if (readErrorCount < kMaxReadErrorLogs) { - LOG(ERROR) << __func__ - << ": no audio pipe yet we're trying to read! (not all errors will be " - "logged)"; - } else { - LOG(ERROR) << __func__ << ": Read errors " << readErrorCount; - } + if (source == nullptr) { + int readErrorCount = mCurrentRoute->notifyReadError(); + if (readErrorCount < kMaxReadErrorLogs) { + LOG(ERROR) << __func__ + << ": no audio pipe yet we're trying to read! (not all errors will be " + "logged)"; } else { - LOG(INFO) << __func__ << ": no data to read yet, providing empty data"; + LOG(ERROR) << __func__ << ": Read errors " << readErrorCount; } const size_t delayUs = static_cast( std::roundf(frameCount * MICROS_PER_SECOND / mStreamConfig.sampleRate)); @@ -306,9 +299,10 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { const size_t delayUs = static_cast(std::roundf(kReadAttemptSleepUs)); char* buff = (char*)buffer; size_t remainingFrames = frameCount; + int availableToRead = source->availableToRead(); - while ((remainingFrames > 0) && (attempts < kMaxReadFailureAttempts)) { - LOG(VERBOSE) << __func__ << ": frames available to read " << source->availableToRead(); + while ((remainingFrames > 0) && (availableToRead > 0) && (attempts < kMaxReadFailureAttempts)) { + LOG(VERBOSE) << __func__ << ": frames available to read " << availableToRead; ssize_t framesRead = source->read(buff, remainingFrames); @@ -317,6 +311,7 @@ size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() { if (framesRead > 0) { remainingFrames -= framesRead; buff += framesRead * mStreamConfig.frameSize; + availableToRead -= framesRead; LOG(VERBOSE) << __func__ << ": (attempts = " << attempts << ") got " << framesRead << " frames, remaining=" << remainingFrames; } else { From 4329d375fe167e3dde7a47b14bec10a275678fb8 Mon Sep 17 00:00:00 2001 From: Shraddha Basantwani Date: Wed, 23 Aug 2023 12:39:15 +0000 Subject: [PATCH 7/9] Audio : Add 3 VTS test cases for remote submix module 1. OutputDoesNotBlockWhenNoInput 2. OutputDoesNotBlockWhenInputStuck 3. OutputAndInput Bug: 286914845 Test: atest AudioModuleRemoteSubmix (cherry picked from https://android-review.googlesource.com/q/commit:343db5e85e5e78e13179872e535c2248989cf081) Merged-In: I19a08bf2bf39131a70a867280c758b5ef001c024 Change-Id: I19a08bf2bf39131a70a867280c758b5ef001c024 --- audio/aidl/vts/ModuleConfig.cpp | 170 ++++++++++--- audio/aidl/vts/ModuleConfig.h | 71 ++++-- .../vts/VtsHalAudioCoreModuleTargetTest.cpp | 229 +++++++++++++++--- 3 files changed, 383 insertions(+), 87 deletions(-) diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp index 7213034bcc..8fdb1552df 100644 --- a/audio/aidl/vts/ModuleConfig.cpp +++ b/audio/aidl/vts/ModuleConfig.cpp @@ -66,15 +66,36 @@ std::optional ModuleConfig::generateOffloadInfoIfNeeded( return {}; } +std::vector +ModuleConfig::getAudioPortsForDeviceTypes(const std::vector& deviceTypes, + const std::string& connection) { + return getAudioPortsForDeviceTypes(mPorts, deviceTypes, connection); +} + // static std::vector ModuleConfig::getBuiltInMicPorts( const std::vector& ports) { + return getAudioPortsForDeviceTypes( + ports, std::vector{AudioDeviceType::IN_MICROPHONE, + AudioDeviceType::IN_MICROPHONE_BACK}); +} + +std::vector +ModuleConfig::getAudioPortsForDeviceTypes( + const std::vector& ports, + const std::vector& deviceTypes, const std::string& connection) { std::vector result; - std::copy_if(ports.begin(), ports.end(), std::back_inserter(result), [](const auto& port) { - const auto type = port.ext.template get().device.type; - return type.connection.empty() && (type.type == AudioDeviceType::IN_MICROPHONE || - type.type == AudioDeviceType::IN_MICROPHONE_BACK); - }); + for (const auto& port : ports) { + if (port.ext.getTag() != AudioPortExt::Tag::device) continue; + const auto type = port.ext.get().device.type; + if (type.connection == connection) { + for (auto deviceType : deviceTypes) { + if (type.type == deviceType) { + result.push_back(port); + } + } + } + } return result; } @@ -119,6 +140,31 @@ std::vector ModuleConfig::getAttachedDevicePorts() const { return result; } +std::vector ModuleConfig::getConnectedExternalDevicePorts() const { + std::vector result; + std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { + return mConnectedExternalSinkDevicePorts.count(port.id) != 0 || + mConnectedExternalSourceDevicePorts.count(port.id) != 0; + }); + return result; +} + +std::set ModuleConfig::getConnectedSinkDevicePorts() const { + std::set result; + result.insert(mAttachedSinkDevicePorts.begin(), mAttachedSinkDevicePorts.end()); + result.insert(mConnectedExternalSinkDevicePorts.begin(), + mConnectedExternalSinkDevicePorts.end()); + return result; +} + +std::set ModuleConfig::getConnectedSourceDevicePorts() const { + std::set result; + result.insert(mAttachedSourceDevicePorts.begin(), mAttachedSourceDevicePorts.end()); + result.insert(mConnectedExternalSourceDevicePorts.begin(), + mConnectedExternalSourceDevicePorts.end()); + return result; +} + std::vector ModuleConfig::getExternalDevicePorts() const { std::vector result; std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), @@ -126,76 +172,77 @@ std::vector ModuleConfig::getExternalDevicePorts() const { return result; } -std::vector ModuleConfig::getInputMixPorts(bool attachedOnly) const { +std::vector ModuleConfig::getInputMixPorts(bool connectedOnly) const { std::vector result; std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { return port.ext.getTag() == AudioPortExt::Tag::mix && port.flags.getTag() == AudioIoFlags::Tag::input && - (!attachedOnly || !getAttachedSourceDevicesPortsForMixPort(port).empty()); + (!connectedOnly || !getConnectedSourceDevicesPortsForMixPort(port).empty()); }); return result; } -std::vector ModuleConfig::getOutputMixPorts(bool attachedOnly) const { +std::vector ModuleConfig::getOutputMixPorts(bool connectedOnly) const { std::vector result; std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) { return port.ext.getTag() == AudioPortExt::Tag::mix && port.flags.getTag() == AudioIoFlags::Tag::output && - (!attachedOnly || !getAttachedSinkDevicesPortsForMixPort(port).empty()); + (!connectedOnly || !getConnectedSinkDevicesPortsForMixPort(port).empty()); }); return result; } -std::vector ModuleConfig::getNonBlockingMixPorts(bool attachedOnly, +std::vector ModuleConfig::getNonBlockingMixPorts(bool connectedOnly, bool singlePort) const { - return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { + return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) { return isBitPositionFlagSet(port.flags.get(), AudioOutputFlags::NON_BLOCKING); }); } -std::vector ModuleConfig::getOffloadMixPorts(bool attachedOnly, bool singlePort) const { - return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { +std::vector ModuleConfig::getOffloadMixPorts(bool connectedOnly, bool singlePort) const { + return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) { return isBitPositionFlagSet(port.flags.get(), AudioOutputFlags::COMPRESS_OFFLOAD); }); } -std::vector ModuleConfig::getPrimaryMixPorts(bool attachedOnly, bool singlePort) const { - return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { +std::vector ModuleConfig::getPrimaryMixPorts(bool connectedOnly, bool singlePort) const { + return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) { return isBitPositionFlagSet(port.flags.get(), AudioOutputFlags::PRIMARY); }); } -std::vector ModuleConfig::getMmapOutMixPorts(bool attachedOnly, bool singlePort) const { - return findMixPorts(false /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { +std::vector ModuleConfig::getMmapOutMixPorts(bool connectedOnly, bool singlePort) const { + return findMixPorts(false /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) { return isBitPositionFlagSet(port.flags.get(), AudioOutputFlags::MMAP_NOIRQ); }); } -std::vector ModuleConfig::getMmapInMixPorts(bool attachedOnly, bool singlePort) const { - return findMixPorts(true /*isInput*/, attachedOnly, singlePort, [&](const AudioPort& port) { +std::vector ModuleConfig::getMmapInMixPorts(bool connectedOnly, bool singlePort) const { + return findMixPorts(true /*isInput*/, connectedOnly, singlePort, [&](const AudioPort& port) { return isBitPositionFlagSet(port.flags.get(), AudioInputFlags::MMAP_NOIRQ); }); } -std::vector ModuleConfig::getAttachedDevicesPortsForMixPort( +std::vector ModuleConfig::getConnectedDevicesPortsForMixPort( bool isInput, const AudioPortConfig& mixPortConfig) const { const auto mixPortIt = findById(mPorts, mixPortConfig.portId); if (mixPortIt != mPorts.end()) { - return getAttachedDevicesPortsForMixPort(isInput, *mixPortIt); + return getConnectedDevicesPortsForMixPort(isInput, *mixPortIt); } return {}; } -std::vector ModuleConfig::getAttachedSinkDevicesPortsForMixPort( +std::vector ModuleConfig::getConnectedSinkDevicesPortsForMixPort( const AudioPort& mixPort) const { std::vector result; + std::set connectedSinkDevicePorts = getConnectedSinkDevicePorts(); for (const auto& route : mRoutes) { - if (mAttachedSinkDevicePorts.count(route.sinkPortId) != 0 && + if ((connectedSinkDevicePorts.count(route.sinkPortId) != 0) && std::find(route.sourcePortIds.begin(), route.sourcePortIds.end(), mixPort.id) != route.sourcePortIds.end()) { const auto devicePortIt = findById(mPorts, route.sinkPortId); @@ -205,13 +252,14 @@ std::vector ModuleConfig::getAttachedSinkDevicesPortsForMixPort( return result; } -std::vector ModuleConfig::getAttachedSourceDevicesPortsForMixPort( +std::vector ModuleConfig::getConnectedSourceDevicesPortsForMixPort( const AudioPort& mixPort) const { std::vector result; + std::set connectedSourceDevicePorts = getConnectedSourceDevicePorts(); for (const auto& route : mRoutes) { if (route.sinkPortId == mixPort.id) { for (const auto srcId : route.sourcePortIds) { - if (mAttachedSourceDevicePorts.count(srcId) != 0) { + if (connectedSourceDevicePorts.count(srcId) != 0) { const auto devicePortIt = findById(mPorts, srcId); if (devicePortIt != mPorts.end()) result.push_back(*devicePortIt); } @@ -221,9 +269,10 @@ std::vector ModuleConfig::getAttachedSourceDevicesPortsForMixPort( return result; } -std::optional ModuleConfig::getSourceMixPortForAttachedDevice() const { +std::optional ModuleConfig::getSourceMixPortForConnectedDevice() const { + std::set connectedSinkDevicePorts = getConnectedSinkDevicePorts(); for (const auto& route : mRoutes) { - if (mAttachedSinkDevicePorts.count(route.sinkPortId) != 0) { + if (connectedSinkDevicePorts.count(route.sinkPortId) != 0) { const auto mixPortIt = findById(mPorts, route.sourcePortIds[0]); if (mixPortIt != mPorts.end()) return *mixPortIt; } @@ -233,7 +282,7 @@ std::optional ModuleConfig::getSourceMixPortForAttachedDevice() const std::optional ModuleConfig::getNonRoutableSrcSinkPair( bool isInput) const { - const auto mixPorts = getMixPorts(isInput, false /*attachedOnly*/); + const auto mixPorts = getMixPorts(isInput, false /*connectedOnly*/); std::set> allowedRoutes; for (const auto& route : mRoutes) { for (const auto srcPortId : route.sourcePortIds) { @@ -243,7 +292,8 @@ std::optional ModuleConfig::getNonRoutableSrcSinkPair auto make_pair = [isInput](auto& device, auto& mix) { return isInput ? std::make_pair(device, mix) : std::make_pair(mix, device); }; - for (const auto portId : isInput ? mAttachedSourceDevicePorts : mAttachedSinkDevicePorts) { + for (const auto portId : + isInput ? getConnectedSourceDevicePorts() : getConnectedSinkDevicePorts()) { const auto devicePortIt = findById(mPorts, portId); if (devicePortIt == mPorts.end()) continue; auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt); @@ -262,10 +312,11 @@ std::optional ModuleConfig::getNonRoutableSrcSinkPair std::optional ModuleConfig::getRoutableSrcSinkPair(bool isInput) const { if (isInput) { + std::set connectedSourceDevicePorts = getConnectedSourceDevicePorts(); for (const auto& route : mRoutes) { auto srcPortIdIt = std::find_if( route.sourcePortIds.begin(), route.sourcePortIds.end(), - [&](const auto& portId) { return mAttachedSourceDevicePorts.count(portId); }); + [&](const auto& portId) { return connectedSourceDevicePorts.count(portId); }); if (srcPortIdIt == route.sourcePortIds.end()) continue; const auto devicePortIt = findById(mPorts, *srcPortIdIt); const auto mixPortIt = findById(mPorts, route.sinkPortId); @@ -276,8 +327,9 @@ std::optional ModuleConfig::getRoutableSrcSinkPair(bo return std::make_pair(devicePortConfig, mixPortConfig.value()); } } else { + std::set connectedSinkDevicePorts = getConnectedSinkDevicePorts(); for (const auto& route : mRoutes) { - if (mAttachedSinkDevicePorts.count(route.sinkPortId) == 0) continue; + if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue; const auto mixPortIt = findById(mPorts, route.sourcePortIds[0]); const auto devicePortIt = findById(mPorts, route.sinkPortId); if (devicePortIt == mPorts.end() || mixPortIt == mPorts.end()) continue; @@ -293,11 +345,12 @@ std::optional ModuleConfig::getRoutableSrcSinkPair(bo std::vector ModuleConfig::getRoutableSrcSinkGroups(bool isInput) const { std::vector result; if (isInput) { + std::set connectedSourceDevicePorts = getConnectedSourceDevicePorts(); for (const auto& route : mRoutes) { std::vector srcPortIds; std::copy_if(route.sourcePortIds.begin(), route.sourcePortIds.end(), std::back_inserter(srcPortIds), [&](const auto& portId) { - return mAttachedSourceDevicePorts.count(portId); + return connectedSourceDevicePorts.count(portId); }); if (srcPortIds.empty()) continue; const auto mixPortIt = findById(mPorts, route.sinkPortId); @@ -317,8 +370,9 @@ std::vector ModuleConfig::getRoutableSrcSinkGroups(b } } } else { + std::set connectedSinkDevicePorts = getConnectedSinkDevicePorts(); for (const auto& route : mRoutes) { - if (mAttachedSinkDevicePorts.count(route.sinkPortId) == 0) continue; + if (connectedSinkDevicePorts.count(route.sinkPortId) == 0) continue; const auto devicePortIt = findById(mPorts, route.sinkPortId); if (devicePortIt == mPorts.end()) continue; auto devicePortConfig = getSingleConfigForDevicePort(*devicePortIt); @@ -352,6 +406,8 @@ std::string ModuleConfig::toString() const { result.append(android::internal::ToString(mAttachedSourceDevicePorts)); result.append("\nExternal device ports: "); result.append(android::internal::ToString(mExternalDevicePorts)); + result.append("\nConnected external device ports: "); + result.append(android::internal::ToString(getConnectedExternalDevicePorts())); result.append("\nRoutes: "); result.append(android::internal::ToString(mRoutes)); return result; @@ -384,10 +440,10 @@ static bool isDynamicProfile(const AudioProfile& profile) { } std::vector ModuleConfig::findMixPorts( - bool isInput, bool attachedOnly, bool singlePort, + bool isInput, bool connectedOnly, bool singlePort, const std::function& pred) const { std::vector result; - const auto mixPorts = getMixPorts(isInput, attachedOnly); + const auto mixPorts = getMixPorts(isInput, connectedOnly); for (auto mixPortIt = mixPorts.begin(); mixPortIt != mixPorts.end();) { mixPortIt = std::find_if(mixPortIt, mixPorts.end(), pred); if (mixPortIt == mixPorts.end()) break; @@ -401,7 +457,7 @@ std::vector ModuleConfig::generateAudioMixPortConfigs( const std::vector& ports, bool isInput, bool singleProfile) const { std::vector result; for (const auto& mixPort : ports) { - if (getAttachedDevicesPortsForMixPort(isInput, mixPort).empty()) { + if (getConnectedDevicesPortsForMixPort(isInput, mixPort).empty()) { continue; } for (const auto& profile : mixPort.profiles) { @@ -443,10 +499,48 @@ std::vector ModuleConfig::generateAudioDevicePortConfigs( return result; } +const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* module, + const AudioPort& port) { + // Update ports and routes + mStatus = module->getAudioPorts(&mPorts); + if (!mStatus.isOk()) return mStatus; + mStatus = module->getAudioRoutes(&mRoutes); + if (!mStatus.isOk()) return mStatus; + + // Validate port is present in module + if (std::find(mPorts.begin(), mPorts.end(), port) == mPorts.end()) { + mStatus = ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + return mStatus; + } + + if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) { + mConnectedExternalSourceDevicePorts.insert(port.id); + } else { + mConnectedExternalSinkDevicePorts.insert(port.id); + } + return mStatus; +} + +const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceDisconnected(IModule* module, + const AudioPort& port) { + // Update ports and routes + mStatus = module->getAudioPorts(&mPorts); + if (!mStatus.isOk()) return mStatus; + mStatus = module->getAudioRoutes(&mRoutes); + if (!mStatus.isOk()) return mStatus; + + if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) { + mConnectedExternalSourceDevicePorts.erase(port.id); + } else { + mConnectedExternalSinkDevicePorts.erase(port.id); + } + return mStatus; +} + bool ModuleConfig::isMmapSupported() const { const std::vector mmapOutMixPorts = - getMmapOutMixPorts(false /*attachedOnly*/, false /*singlePort*/); + getMmapOutMixPorts(false /*connectedOnly*/, false /*singlePort*/); const std::vector mmapInMixPorts = - getMmapInMixPorts(false /*attachedOnly*/, false /*singlePort*/); + getMmapInMixPorts(false /*connectedOnly*/, false /*singlePort*/); return !mmapOutMixPorts.empty() || !mmapInMixPorts.empty(); } diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h index 6a220756f3..bce1de175f 100644 --- a/audio/aidl/vts/ModuleConfig.h +++ b/audio/aidl/vts/ModuleConfig.h @@ -37,6 +37,14 @@ class ModuleConfig { static std::optional generateOffloadInfoIfNeeded( const aidl::android::media::audio::common::AudioPortConfig& portConfig); + + std::vector getAudioPortsForDeviceTypes( + const std::vector& deviceTypes, + const std::string& connection = ""); + static std::vector getAudioPortsForDeviceTypes( + const std::vector& ports, + const std::vector& deviceTypes, + const std::string& connection = ""); static std::vector getBuiltInMicPorts( const std::vector& ports); @@ -45,45 +53,55 @@ class ModuleConfig { std::string getError() const { return mStatus.getMessage(); } std::vector getAttachedDevicePorts() const; + std::vector getConnectedExternalDevicePorts() + const; + std::set getConnectedSinkDevicePorts() const; + std::set getConnectedSourceDevicePorts() const; std::vector getAttachedMicrophonePorts() const { return getBuiltInMicPorts(getAttachedDevicePorts()); } std::vector getExternalDevicePorts() const; std::vector getInputMixPorts( - bool attachedOnly) const; + bool connectedOnly /*Permanently attached and connected external devices*/) const; std::vector getOutputMixPorts( - bool attachedOnly) const; + bool connectedOnly /*Permanently attached and connected external devices*/) const; std::vector getMixPorts( - bool isInput, bool attachedOnly) const { - return isInput ? getInputMixPorts(attachedOnly) : getOutputMixPorts(attachedOnly); + bool isInput, + bool connectedOnly /*Permanently attached and connected external devices*/) const { + return isInput ? getInputMixPorts(connectedOnly) : getOutputMixPorts(connectedOnly); } std::vector getNonBlockingMixPorts( - bool attachedOnly, bool singlePort) const; + bool connectedOnly /*Permanently attached and connected external devices*/, + bool singlePort) const; std::vector getOffloadMixPorts( - bool attachedOnly, bool singlePort) const; + bool connectedOnly /*Permanently attached and connected external devices*/, + bool singlePort) const; std::vector getPrimaryMixPorts( - bool attachedOnly, bool singlePort) const; + bool connectedOnly /*Permanently attached and connected external devices*/, + bool singlePort) const; std::vector getMmapOutMixPorts( - bool attachedOnly, bool singlePort) const; + bool connectedOnly /*Permanently attached and connected external devices*/, + bool singlePort) const; std::vector getMmapInMixPorts( - bool attachedOnly, bool singlePort) const; + bool connectedOnly /*Permanently attached and connected external devices*/, + bool singlePort) const; - std::vector getAttachedDevicesPortsForMixPort( + std::vector getConnectedDevicesPortsForMixPort( bool isInput, const aidl::android::media::audio::common::AudioPort& mixPort) const { - return isInput ? getAttachedSourceDevicesPortsForMixPort(mixPort) - : getAttachedSinkDevicesPortsForMixPort(mixPort); + return isInput ? getConnectedSourceDevicesPortsForMixPort(mixPort) + : getConnectedSinkDevicesPortsForMixPort(mixPort); } - std::vector getAttachedDevicesPortsForMixPort( + std::vector getConnectedDevicesPortsForMixPort( bool isInput, const aidl::android::media::audio::common::AudioPortConfig& mixPortConfig) const; std::vector - getAttachedSinkDevicesPortsForMixPort( + getConnectedSinkDevicesPortsForMixPort( const aidl::android::media::audio::common::AudioPort& mixPort) const; std::vector - getAttachedSourceDevicesPortsForMixPort( + getConnectedSourceDevicesPortsForMixPort( const aidl::android::media::audio::common::AudioPort& mixPort) const; std::optional - getSourceMixPortForAttachedDevice() const; + getSourceMixPortForConnectedDevice() const; std::optional getNonRoutableSrcSinkPair(bool isInput) const; std::optional getRoutableSrcSinkPair(bool isInput) const; @@ -96,15 +114,15 @@ class ModuleConfig { std::vector getPortConfigsForMixPorts() const { auto inputs = - generateAudioMixPortConfigs(getInputMixPorts(false /*attachedOnly*/), true, false); - auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(false /*attachedOnly*/), false, - false); + generateAudioMixPortConfigs(getInputMixPorts(false /*connectedOnly*/), true, false); + auto outputs = generateAudioMixPortConfigs(getOutputMixPorts(false /*connectedOnly*/), + false, false); inputs.insert(inputs.end(), outputs.begin(), outputs.end()); return inputs; } std::vector getPortConfigsForMixPorts( bool isInput) const { - return generateAudioMixPortConfigs(getMixPorts(isInput, false /*attachedOnly*/), isInput, + return generateAudioMixPortConfigs(getMixPorts(isInput, false /*connectedOnly*/), isInput, false); } std::vector getPortConfigsForMixPorts( @@ -114,7 +132,7 @@ class ModuleConfig { std::optional getSingleConfigForMixPort( bool isInput) const { const auto config = generateAudioMixPortConfigs( - getMixPorts(isInput, false /*attachedOnly*/), isInput, true); + getMixPorts(isInput, false /*connectedOnly*/), isInput, true); if (!config.empty()) { return *config.begin(); } @@ -139,13 +157,20 @@ class ModuleConfig { return *config.begin(); } + const ndk::ScopedAStatus& onExternalDeviceConnected( + aidl::android::hardware::audio::core::IModule* module, + const aidl::android::media::audio::common::AudioPort& port); + const ndk::ScopedAStatus& onExternalDeviceDisconnected( + aidl::android::hardware::audio::core::IModule* module, + const aidl::android::media::audio::common::AudioPort& port); + bool isMmapSupported() const; std::string toString() const; private: std::vector findMixPorts( - bool isInput, bool attachedOnly, bool singlePort, + bool isInput, bool connectedOnly, bool singlePort, const std::function& pred) const; std::vector generateAudioMixPortConfigs( @@ -167,5 +192,7 @@ class ModuleConfig { std::set mAttachedSinkDevicePorts; std::set mAttachedSourceDevicePorts; std::set mExternalDevicePorts; + std::set mConnectedExternalSinkDevicePorts; + std::set mConnectedExternalSourceDevicePorts; std::vector mRoutes; }; diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp index 8a8998e10c..025056e132 100644 --- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp @@ -525,13 +525,21 @@ class WithDevicePortConnectedState { EXPECT_IS_OK(mModule->disconnectExternalDevice(getId())) << "when disconnecting device port ID " << getId(); } + if (mModuleConfig != nullptr) { + EXPECT_IS_OK(mModuleConfig->onExternalDeviceDisconnected(mModule, mConnectedPort)) + << "when external device disconnected"; + } } - void SetUp(IModule* module) { + void SetUp(IModule* module, ModuleConfig* moduleConfig) { ASSERT_IS_OK(module->connectExternalDevice(mIdAndData, &mConnectedPort)) << "when connecting device port ID & data " << mIdAndData.toString(); ASSERT_NE(mIdAndData.id, getId()) << "ID of the connected port must not be the same as the ID of the template port"; + ASSERT_NE(moduleConfig, nullptr); + ASSERT_IS_OK(moduleConfig->onExternalDeviceConnected(module, mConnectedPort)) + << "when external device connected"; mModule = module; + mModuleConfig = moduleConfig; } int32_t getId() const { return mConnectedPort.id; } const AudioPort& get() { return mConnectedPort; } @@ -539,6 +547,7 @@ class WithDevicePortConnectedState { private: const AudioPort mIdAndData; IModule* mModule = nullptr; + ModuleConfig* mModuleConfig = nullptr; AudioPort mConnectedPort; }; @@ -1422,7 +1431,7 @@ TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) { for (const auto& port : ports) { AudioPort portWithData = GenerateUniqueDeviceAddress(port); WithDevicePortConnectedState portConnected(portWithData); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); const int32_t connectedPortId = portConnected.getId(); ASSERT_NE(portWithData.id, connectedPortId); ASSERT_EQ(portWithData.ext.getTag(), portConnected.get().ext.getTag()); @@ -1526,7 +1535,7 @@ TEST_P(AudioCoreModule, ResetAudioPortConfigToInitialValue) { TEST_P(AudioCoreModule, SetAudioPortConfigSuggestedConfig) { ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); - auto srcMixPort = moduleConfig->getSourceMixPortForAttachedDevice(); + auto srcMixPort = moduleConfig->getSourceMixPortForConnectedDevice(); if (!srcMixPort.has_value()) { GTEST_SKIP() << "No mix port for attached output devices"; } @@ -1578,7 +1587,7 @@ TEST_P(AudioCoreModule, SetAllExternalDevicePortConfigs) { } for (const auto& port : ports) { WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); ASSERT_NO_FATAL_FAILURE( ApplyEveryConfig(moduleConfig->getPortConfigsForDevicePort(portConnected.get()))); } @@ -1648,7 +1657,7 @@ TEST_P(AudioCoreModule, TryChangingConnectionSimulationMidway) { GTEST_SKIP() << "No external devices in the module."; } WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(*ports.begin())); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); ModuleDebug midwayDebugChange = debug->flags(); midwayDebugChange.simulateDeviceConnections = false; EXPECT_STATUS(EX_ILLEGAL_STATE, module->setModuleDebug(midwayDebugChange)) @@ -1703,7 +1712,7 @@ TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceTwice) { << "when disconnecting already disconnected device port ID " << port.id; AudioPort portWithData = GenerateUniqueDeviceAddress(port); WithDevicePortConnectedState portConnected(portWithData); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->connectExternalDevice(portConnected.get(), &ignored)) << "when trying to connect a connected device port " @@ -1725,7 +1734,7 @@ TEST_P(AudioCoreModule, DisconnectExternalDeviceNonResetPortConfig) { } for (const auto& port : ports) { WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); const auto portConfig = moduleConfig->getSingleConfigForDevicePort(portConnected.get()); { WithAudioPortConfig config(portConfig); @@ -1753,7 +1762,7 @@ TEST_P(AudioCoreModule, ExternalDevicePortRoutes) { int32_t connectedPortId; { WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); connectedPortId = portConnected.getId(); std::vector connectedPortRoutes; ASSERT_IS_OK(module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes)) @@ -1794,7 +1803,7 @@ TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) { } for (const auto& port : externalDevicePorts) { WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port)); - ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get())); + ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get())); std::vector routes; ASSERT_IS_OK(module->getAudioRoutesForAudioPort(portConnected.getId(), &routes)); std::vector allPorts; @@ -2459,7 +2468,7 @@ class AudioStream : public AudioCoreModule { void OpenOverMaxCount() { constexpr bool isInput = IOTraits::is_input; - auto ports = moduleConfig->getMixPorts(isInput, true /*attachedOnly*/); + auto ports = moduleConfig->getMixPorts(isInput, true /*connectedOnly*/); bool hasSingleRun = false; for (const auto& port : ports) { const size_t maxStreamCount = port.ext.get().maxOpenStreamCount; @@ -2580,7 +2589,7 @@ class AudioStream : public AudioCoreModule { void HwGainHwVolume() { const auto ports = - moduleConfig->getMixPorts(IOTraits::is_input, true /*attachedOnly*/); + moduleConfig->getMixPorts(IOTraits::is_input, true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No mix ports"; } @@ -2619,7 +2628,7 @@ class AudioStream : public AudioCoreModule { // it as an invalid argument, or say that offloaded effects are not supported. void AddRemoveEffectInvalidArguments() { const auto ports = - moduleConfig->getMixPorts(IOTraits::is_input, true /*attachedOnly*/); + moduleConfig->getMixPorts(IOTraits::is_input, true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No mix ports"; } @@ -2742,7 +2751,7 @@ TEST_P(AudioStreamIn, ActiveMicrophones) { if (!status.isOk()) { GTEST_SKIP() << "Microphone info is not supported"; } - const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } @@ -2759,7 +2768,7 @@ TEST_P(AudioStreamIn, ActiveMicrophones) { "non-empty list of active microphones"; } if (auto micDevicePorts = ModuleConfig::getBuiltInMicPorts( - moduleConfig->getAttachedSourceDevicesPortsForMixPort(port)); + moduleConfig->getConnectedSourceDevicesPortsForMixPort(port)); !micDevicePorts.empty()) { auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(micDevicePorts[0]); WithAudioPatch patch(true /*isInput*/, stream.getPortConfig(), devicePortConfig); @@ -2791,7 +2800,7 @@ TEST_P(AudioStreamIn, ActiveMicrophones) { TEST_P(AudioStreamIn, MicrophoneDirection) { using MD = IStreamIn::MicrophoneDirection; - const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } @@ -2814,7 +2823,7 @@ TEST_P(AudioStreamIn, MicrophoneDirection) { } TEST_P(AudioStreamIn, MicrophoneFieldDimension) { - const auto ports = moduleConfig->getInputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getInputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No input mix ports for attached devices"; } @@ -2846,7 +2855,7 @@ TEST_P(AudioStreamIn, MicrophoneFieldDimension) { TEST_P(AudioStreamOut, OpenTwicePrimary) { const auto mixPorts = - moduleConfig->getPrimaryMixPorts(true /*attachedOnly*/, true /*singlePort*/); + moduleConfig->getPrimaryMixPorts(true /*connectedOnly*/, true /*singlePort*/); if (mixPorts.empty()) { GTEST_SKIP() << "No primary mix port which could be routed to attached devices"; } @@ -2857,7 +2866,7 @@ TEST_P(AudioStreamOut, OpenTwicePrimary) { TEST_P(AudioStreamOut, RequireOffloadInfo) { const auto offloadMixPorts = - moduleConfig->getOffloadMixPorts(true /*attachedOnly*/, true /*singlePort*/); + moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, true /*singlePort*/); if (offloadMixPorts.empty()) { GTEST_SKIP() << "No mix port for compressed offload that could be routed to attached devices"; @@ -2879,7 +2888,7 @@ TEST_P(AudioStreamOut, RequireOffloadInfo) { TEST_P(AudioStreamOut, RequireAsyncCallback) { const auto nonBlockingMixPorts = - moduleConfig->getNonBlockingMixPorts(true /*attachedOnly*/, true /*singlePort*/); + moduleConfig->getNonBlockingMixPorts(true /*connectedOnly*/, true /*singlePort*/); if (nonBlockingMixPorts.empty()) { GTEST_SKIP() << "No mix port for non-blocking output that could be routed to attached devices"; @@ -2902,7 +2911,7 @@ TEST_P(AudioStreamOut, RequireAsyncCallback) { } TEST_P(AudioStreamOut, AudioDescriptionMixLevel) { - const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No output mix ports"; } @@ -2930,7 +2939,7 @@ TEST_P(AudioStreamOut, AudioDescriptionMixLevel) { } TEST_P(AudioStreamOut, DualMonoMode) { - const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No output mix ports"; } @@ -2954,7 +2963,7 @@ TEST_P(AudioStreamOut, DualMonoMode) { } TEST_P(AudioStreamOut, LatencyMode) { - const auto ports = moduleConfig->getOutputMixPorts(true /*attachedOnly*/); + const auto ports = moduleConfig->getOutputMixPorts(true /*connectedOnly*/); if (ports.empty()) { GTEST_SKIP() << "No output mix ports"; } @@ -2996,7 +3005,7 @@ TEST_P(AudioStreamOut, LatencyMode) { TEST_P(AudioStreamOut, PlaybackRate) { static const auto kStatuses = {EX_NONE, EX_UNSUPPORTED_OPERATION}; const auto offloadMixPorts = - moduleConfig->getOffloadMixPorts(true /*attachedOnly*/, false /*singlePort*/); + moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, false /*singlePort*/); if (offloadMixPorts.empty()) { GTEST_SKIP() << "No mix port for compressed offload that could be routed to attached devices"; @@ -3066,7 +3075,7 @@ TEST_P(AudioStreamOut, PlaybackRate) { TEST_P(AudioStreamOut, SelectPresentation) { static const auto kStatuses = {EX_ILLEGAL_ARGUMENT, EX_UNSUPPORTED_OPERATION}; const auto offloadMixPorts = - moduleConfig->getOffloadMixPorts(true /*attachedOnly*/, false /*singlePort*/); + moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, false /*singlePort*/); if (offloadMixPorts.empty()) { GTEST_SKIP() << "No mix port for compressed offload that could be routed to attached devices"; @@ -3088,7 +3097,7 @@ TEST_P(AudioStreamOut, SelectPresentation) { TEST_P(AudioStreamOut, UpdateOffloadMetadata) { const auto offloadMixPorts = - moduleConfig->getOffloadMixPorts(true /*attachedOnly*/, false /*singlePort*/); + moduleConfig->getOffloadMixPorts(true /*connectedOnly*/, false /*singlePort*/); if (offloadMixPorts.empty()) { GTEST_SKIP() << "No mix port for compressed offload that could be routed to attached devices"; @@ -3301,7 +3310,7 @@ class AudioStreamIo : public AudioCoreModuleBase, void RunStreamIoCommandsImplSeq1(const AudioPortConfig& portConfig, std::shared_ptr commandsAndStates, bool validatePositionIncrease) { - auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort( + auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort( IOTraits::is_input, portConfig); ASSERT_FALSE(devicePorts.empty()); auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]); @@ -3341,7 +3350,7 @@ class AudioStreamIo : public AudioCoreModuleBase, typename IOTraits::Worker worker(*stream.getContext(), &driver, stream.getEventReceiver()); - auto devicePorts = moduleConfig->getAttachedDevicesPortsForMixPort( + auto devicePorts = moduleConfig->getConnectedDevicesPortsForMixPort( IOTraits::is_input, portConfig); ASSERT_FALSE(devicePorts.empty()); auto devicePortConfig = moduleConfig->getSingleConfigForDevicePort(devicePorts[0]); @@ -4006,6 +4015,172 @@ INSTANTIATE_TEST_SUITE_P(AudioPatchTest, AudioModulePatch, android::PrintInstanceNameToString); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModulePatch); +static std::vector getRemoteSubmixModuleInstance() { + auto instances = android::getAidlHalInstanceNames(IModule::descriptor); + for (auto instance : instances) { + if (instance.find("r_submix") != std::string::npos) + return (std::vector{instance}); + } + return {}; +} + +template +class WithRemoteSubmix { + public: + WithRemoteSubmix() = default; + WithRemoteSubmix(AudioDeviceAddress address) : mAddress(address) {} + WithRemoteSubmix(const WithRemoteSubmix&) = delete; + WithRemoteSubmix& operator=(const WithRemoteSubmix&) = delete; + std::optional getAudioPort() { + AudioDeviceType deviceType = IOTraits::is_input ? AudioDeviceType::IN_SUBMIX + : AudioDeviceType::OUT_SUBMIX; + auto ports = mModuleConfig->getAudioPortsForDeviceTypes( + std::vector{deviceType}, + AudioDeviceDescription::CONNECTION_VIRTUAL); + if (!ports.empty()) return ports.front(); + return {}; + } + /* Connect remote submix external device */ + void SetUpPortConnection() { + auto port = getAudioPort(); + ASSERT_TRUE(port.has_value()) << "Device AudioPort for remote submix not found"; + if (mAddress.has_value()) { + port.value().ext.template get().device.address = + mAddress.value(); + } else { + port = GenerateUniqueDeviceAddress(port.value()); + } + mConnectedPort = std::make_unique(port.value()); + ASSERT_NO_FATAL_FAILURE(mConnectedPort->SetUp(mModule, mModuleConfig)); + } + AudioDeviceAddress getAudioDeviceAddress() { + if (!mAddress.has_value()) { + mAddress = mConnectedPort->get() + .ext.template get() + .device.address; + } + return mAddress.value(); + } + /* Get mix port config for stream and setup patch for it. */ + void SetupPatch() { + const auto portConfig = + mModuleConfig->getSingleConfigForMixPort(IOTraits::is_input); + if (!portConfig.has_value()) { + LOG(DEBUG) << __func__ << ": portConfig not found"; + mSkipTest = true; + return; + } + auto devicePortConfig = mModuleConfig->getSingleConfigForDevicePort(mConnectedPort->get()); + mPatch = std::make_unique(IOTraits::is_input, portConfig.value(), + devicePortConfig); + ASSERT_NO_FATAL_FAILURE(mPatch->SetUp(mModule)); + } + void SetUp(IModule* module, ModuleConfig* moduleConfig) { + mModule = module; + mModuleConfig = moduleConfig; + ASSERT_NO_FATAL_FAILURE(SetUpPortConnection()); + ASSERT_NO_FATAL_FAILURE(SetupPatch()); + if (!mSkipTest) { + // open stream + mStream = std::make_unique>( + mPatch->getPortConfig(IOTraits::is_input)); + ASSERT_NO_FATAL_FAILURE( + mStream->SetUp(mModule, AudioCoreModuleBase::kDefaultBufferSizeFrames)); + } + } + void sendBurstCommands() { + const StreamContext* context = mStream->getContext(); + StreamLogicDefaultDriver driver(makeBurstCommands(true), context->getFrameSizeBytes()); + typename IOTraits::Worker worker(*context, &driver, mStream->getEventReceiver()); + + LOG(DEBUG) << __func__ << ": starting worker..."; + ASSERT_TRUE(worker.start()); + LOG(DEBUG) << __func__ << ": joining worker..."; + worker.join(); + EXPECT_FALSE(worker.hasError()) << worker.getError(); + EXPECT_EQ("", driver.getUnexpectedStateTransition()); + if (IOTraits::is_input) { + EXPECT_TRUE(driver.hasObservablePositionIncrease()); + } + EXPECT_FALSE(driver.hasRetrogradeObservablePosition()); + } + bool skipTest() { return mSkipTest; } + + private: + bool mSkipTest = false; + IModule* mModule = nullptr; + ModuleConfig* mModuleConfig = nullptr; + std::optional mAddress; + std::unique_ptr mConnectedPort; + std::unique_ptr mPatch; + std::unique_ptr> mStream; +}; + +class AudioModuleRemoteSubmix : public AudioCoreModule { + public: + void SetUp() override { + ASSERT_NO_FATAL_FAILURE(AudioCoreModule::SetUp()); + ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig()); + } + + void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); } +}; + +TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenNoInput) { + // open output stream + WithRemoteSubmix streamOut; + ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); + if (streamOut.skipTest()) { + GTEST_SKIP() << "No mix port for attached devices"; + } + // write something to stream + ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); +} + +TEST_P(AudioModuleRemoteSubmix, OutputDoesNotBlockWhenInputStuck) { + // open output stream + WithRemoteSubmix streamOut; + ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); + if (streamOut.skipTest()) { + GTEST_SKIP() << "No mix port for attached devices"; + } + + // open input stream + WithRemoteSubmix streamIn(streamOut.getAudioDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get())); + if (streamIn.skipTest()) { + GTEST_SKIP() << "No mix port for attached devices"; + } + + // write something to stream + ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); +} + +TEST_P(AudioModuleRemoteSubmix, OutputAndInput) { + // open output stream + WithRemoteSubmix streamOut; + ASSERT_NO_FATAL_FAILURE(streamOut.SetUp(module.get(), moduleConfig.get())); + if (streamOut.skipTest()) { + GTEST_SKIP() << "No mix port for attached devices"; + } + + // open input stream + WithRemoteSubmix streamIn(streamOut.getAudioDeviceAddress()); + ASSERT_NO_FATAL_FAILURE(streamIn.SetUp(module.get(), moduleConfig.get())); + if (streamIn.skipTest()) { + GTEST_SKIP() << "No mix port for attached devices"; + } + + // write something to stream + ASSERT_NO_FATAL_FAILURE(streamOut.sendBurstCommands()); + // read from input stream + ASSERT_NO_FATAL_FAILURE(streamIn.sendBurstCommands()); +} + +INSTANTIATE_TEST_SUITE_P(AudioModuleRemoteSubmixTest, AudioModuleRemoteSubmix, + ::testing::ValuesIn(getRemoteSubmixModuleInstance())); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioModuleRemoteSubmix); + class TestExecutionTracer : public ::testing::EmptyTestEventListener { public: void OnTestStart(const ::testing::TestInfo& test_info) override { From 1f8c3b6f3f0d0a255ac20c134c54bce75297308e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 12 Sep 2023 12:40:43 -0700 Subject: [PATCH 8/9] audio: Mitigate double receiving of the "exit" command In rare cases, a worker thread from a new stream created via StreamSwitcher can read again from the command FMQ the "exit" command which was sent to the worker of the previous stream. The underlying reason for that has to be investigated. For now, mitigate the issue by salting the cookie of the "exit" command with the worker's TID. Bug: 300130515 Test: atest VtsHalAudioCoreTargetTest (cherry picked from https://android-review.googlesource.com/q/commit:0e7bcae462e05dbdf4ef797365f9414437914bd7) Merged-In: Ie7d2e847e8b39414ffd31afd64e32d4c9a292c03 Change-Id: Ie7d2e847e8b39414ffd31afd64e32d4c9a292c03 --- audio/aidl/default/Stream.cpp | 16 +++++++++++++--- audio/aidl/default/include/core-impl/Stream.h | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index af89f5fce8..f7298c0286 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #define LOG_TAG "AHAL_Stream" #include #include @@ -94,6 +96,14 @@ void StreamContext::reset() { mDataMQ.reset(); } +pid_t StreamWorkerCommonLogic::getTid() const { +#if defined(__ANDROID__) + return pthread_gettid_np(pthread_self()); +#else + return 0; +#endif +} + std::string StreamWorkerCommonLogic::init() { if (mContext->getCommandMQ() == nullptr) return "Command MQ is null"; if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null"; @@ -164,7 +174,7 @@ StreamInWorkerLogic::Status StreamInWorkerLogic::cycle() { switch (command.getTag()) { case Tag::halReservedExit: if (const int32_t cookie = command.get(); - cookie == mContext->getInternalCommandCookie()) { + cookie == (mContext->getInternalCommandCookie() ^ getTid())) { mDriver->shutdown(); setClosed(); // This is an internal command, no need to reply. @@ -384,7 +394,7 @@ StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() { switch (command.getTag()) { case Tag::halReservedExit: if (const int32_t cookie = command.get(); - cookie == mContext->getInternalCommandCookie()) { + cookie == (mContext->getInternalCommandCookie() ^ getTid())) { mDriver->shutdown(); setClosed(); // This is an internal command, no need to reply. @@ -717,7 +727,7 @@ void StreamCommonImpl::stopWorker() { if (auto commandMQ = mContext.getCommandMQ(); commandMQ != nullptr) { LOG(DEBUG) << __func__ << ": asking the worker to exit..."; auto cmd = StreamDescriptor::Command::make( - mContext.getInternalCommandCookie()); + mContext.getInternalCommandCookie() ^ mWorker->getTid()); // Note: never call 'pause' and 'resume' methods of StreamWorker // in the HAL implementation. These methods are to be used by // the client side only. Preventing the worker loop from running diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index a02655f1b7..88fddec233 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -223,6 +223,7 @@ class StreamWorkerCommonLogic : public ::android::hardware::audio::common::Strea : mContext(context), mDriver(driver), mTransientStateDelayMs(context->getTransientStateDelayMs()) {} + pid_t getTid() const; std::string init() override; void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const; void populateReplyWrongState(StreamDescriptor::Reply* reply, From 426b8762667d296fcb4e48b08c4c822875ddbca6 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Fri, 15 Sep 2023 18:53:42 -0700 Subject: [PATCH 9/9] audio: Improve testing of point-to-point connections Point-to-point connections (analog, HDMI, SPDIF) do not use a device address. Reflect that in `GenerateUniqueDeviceAddress`. Add an analog headset into the test configuration. Bug: 300648357 Test: atest VtsHalAudioCoreTargetTest (cherry picked from https://android-review.googlesource.com/q/commit:fe47b00628a5a9092455d370709ed63cc6b9305a) Merged-In: Id1d0b1b60df40c2474fe3151067152b8e0a261c3 Change-Id: Id1d0b1b60df40c2474fe3151067152b8e0a261c3 --- audio/aidl/default/Configuration.cpp | 26 ++++++++++- .../vts/VtsHalAudioCoreModuleTargetTest.cpp | 46 +++++++++++-------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/audio/aidl/default/Configuration.cpp b/audio/aidl/default/Configuration.cpp index 9131935f87..b9f1131eca 100644 --- a/audio/aidl/default/Configuration.cpp +++ b/audio/aidl/default/Configuration.cpp @@ -461,6 +461,10 @@ std::unique_ptr getUsbConfiguration() { // - no profiles specified // * "Test In", IN_AFE_PROXY // - no profiles specified +// * "Wired Headset", OUT_HEADSET +// - profile PCM 24-bit; STEREO; 48000 +// * "Wired Headset Mic", IN_HEADSET +// - profile PCM 24-bit; MONO; 48000 // // Mix ports: // * "test output", 1 max open, 1 max active stream @@ -476,7 +480,8 @@ std::unique_ptr getUsbConfiguration() { // // Routes: // "test output", "test fast output", "test compressed offload" -> "Test Out" -// "Test In" -> "test input" +// "test output" -> "Wired Headset" +// "Test In", "Wired Headset Mic" -> "test input" // // Initial port configs: // * "Test Out" device port: PCM 24-bit; STEREO; 48000 @@ -496,6 +501,14 @@ std::unique_ptr getStubConfiguration() { AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false, createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0))); + AudioPort headsetOutDevice = + 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, createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)); c.ports.push_back(testInDevice); @@ -504,6 +517,14 @@ std::unique_ptr getStubConfiguration() { AudioChannelLayout::LAYOUT_MONO, 48000, 0, true, createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0))); + AudioPort headsetInDevice = + 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 AudioPort testOutMix = @@ -549,7 +570,8 @@ std::unique_ptr getStubConfiguration() { c.routes.push_back( createRoute({testOutMix, testFastOutMix, compressedOffloadOutMix}, testOutDevice)); - c.routes.push_back(createRoute({testInDevice}, testInMIx)); + c.routes.push_back(createRoute({testOutMix}, headsetOutDevice)); + c.routes.push_back(createRoute({testInDevice, headsetInDevice}, testInMIx)); c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end()); diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp index 025056e132..b1eecd2141 100644 --- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp @@ -144,28 +144,36 @@ AudioDeviceAddress::Tag suggestDeviceAddressTag(const AudioDeviceDescription& de } AudioPort GenerateUniqueDeviceAddress(const AudioPort& port) { + // Point-to-point connections do not use addresses. + static const std::set kPointToPointConnections = { + AudioDeviceDescription::CONNECTION_ANALOG, AudioDeviceDescription::CONNECTION_HDMI, + AudioDeviceDescription::CONNECTION_HDMI_ARC, + AudioDeviceDescription::CONNECTION_HDMI_EARC, AudioDeviceDescription::CONNECTION_SPDIF}; static int nextId = 0; using Tag = AudioDeviceAddress::Tag; + const auto& deviceDescription = port.ext.get().device.type; AudioDeviceAddress address; - switch (suggestDeviceAddressTag(port.ext.get().device.type)) { - case Tag::id: - address = AudioDeviceAddress::make(std::to_string(++nextId)); - break; - case Tag::mac: - address = AudioDeviceAddress::make( - std::vector{1, 2, 3, 4, 5, static_cast(++nextId & 0xff)}); - break; - case Tag::ipv4: - address = AudioDeviceAddress::make( - std::vector{192, 168, 0, static_cast(++nextId & 0xff)}); - break; - case Tag::ipv6: - address = AudioDeviceAddress::make(std::vector{ - 0xfc00, 0x0123, 0x4567, 0x89ab, 0xcdef, 0, 0, ++nextId & 0xffff}); - break; - case Tag::alsa: - address = AudioDeviceAddress::make(std::vector{1, ++nextId}); - break; + if (kPointToPointConnections.count(deviceDescription.connection) == 0) { + switch (suggestDeviceAddressTag(deviceDescription)) { + case Tag::id: + address = AudioDeviceAddress::make(std::to_string(++nextId)); + break; + case Tag::mac: + address = AudioDeviceAddress::make( + std::vector{1, 2, 3, 4, 5, static_cast(++nextId & 0xff)}); + break; + case Tag::ipv4: + address = AudioDeviceAddress::make( + std::vector{192, 168, 0, static_cast(++nextId & 0xff)}); + break; + case Tag::ipv6: + address = AudioDeviceAddress::make(std::vector{ + 0xfc00, 0x0123, 0x4567, 0x89ab, 0xcdef, 0, 0, ++nextId & 0xffff}); + break; + case Tag::alsa: + address = AudioDeviceAddress::make(std::vector{1, ++nextId}); + break; + } } AudioPort result = port; result.ext.get().device.address = std::move(address);