From 95f9232923139f38d802054f5df27a73f1328a09 Mon Sep 17 00:00:00 2001 From: Shraddha Basantwani Date: Tue, 22 Aug 2023 15:07:16 +0000 Subject: [PATCH 1/2] 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 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 343db5e85e5e78e13179872e535c2248989cf081 Mon Sep 17 00:00:00 2001 From: Shraddha Basantwani Date: Wed, 23 Aug 2023 12:39:15 +0000 Subject: [PATCH 2/2] Audio : Add 3 VTS test cases for remote submix module 1. OutputDoesNotBlockWhenNoInput 2. OutputDoesNotBlockWhenInputStuck 3. OutputAndInput Bug: 286914845 Test: atest AudioModuleRemoteSubmix 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 94b33d3830..8958357ba0 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]); @@ -4001,6 +4010,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 {