Merge "audio: Fix handling of external devices disconnection" into main
This commit is contained in:
commit
a39e854ef5
6 changed files with 224 additions and 73 deletions
|
@ -517,7 +517,7 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
|
||||
connectedPort.id = ++getConfig().nextPortId;
|
||||
auto [connectedPortsIt, _] =
|
||||
mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::vector<int32_t>()));
|
||||
mConnectedDevicePorts.insert(std::pair(connectedPort.id, std::set<int32_t>()));
|
||||
LOG(DEBUG) << __func__ << ": template port " << templateId << " external device connected, "
|
||||
<< "connected port ID " << connectedPort.id;
|
||||
ports.push_back(connectedPort);
|
||||
|
@ -550,9 +550,21 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
|
|||
// of all profiles from all routable dynamic device ports would be more involved.
|
||||
for (const auto mixPortId : routablePortIds) {
|
||||
auto portsIt = findById<AudioPort>(ports, mixPortId);
|
||||
if (portsIt != ports.end() && portsIt->profiles.empty()) {
|
||||
portsIt->profiles = connectedPort.profiles;
|
||||
connectedPortsIt->second.push_back(portsIt->id);
|
||||
if (portsIt != ports.end()) {
|
||||
if (portsIt->profiles.empty()) {
|
||||
portsIt->profiles = connectedPort.profiles;
|
||||
connectedPortsIt->second.insert(portsIt->id);
|
||||
} else {
|
||||
// Check if profiles are non empty because they were populated by
|
||||
// a previous connection. Otherwise, it means that they are not empty because
|
||||
// the mix port has static profiles.
|
||||
for (const auto cp : mConnectedDevicePorts) {
|
||||
if (cp.second.count(portsIt->id) > 0) {
|
||||
connectedPortsIt->second.insert(portsIt->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*_aidl_return = std::move(connectedPort);
|
||||
|
@ -607,13 +619,20 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
|
|||
}
|
||||
}
|
||||
|
||||
for (const auto mixPortId : connectedPortsIt->second) {
|
||||
// Clear profiles for mix ports that are not connected to any other ports.
|
||||
std::set<int32_t> mixPortsToClear = std::move(connectedPortsIt->second);
|
||||
mConnectedDevicePorts.erase(connectedPortsIt);
|
||||
for (const auto& connectedPort : mConnectedDevicePorts) {
|
||||
for (int32_t mixPortId : connectedPort.second) {
|
||||
mixPortsToClear.erase(mixPortId);
|
||||
}
|
||||
}
|
||||
for (int32_t mixPortId : mixPortsToClear) {
|
||||
auto mixPortIt = findById<AudioPort>(ports, mixPortId);
|
||||
if (mixPortIt != ports.end()) {
|
||||
mixPortIt->profiles = {};
|
||||
}
|
||||
}
|
||||
mConnectedDevicePorts.erase(connectedPortsIt);
|
||||
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ class Module : public BnModule {
|
|||
// ids of device ports created at runtime via 'connectExternalDevice'.
|
||||
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
|
||||
// the connected port. This list can be empty, thus an int->int multimap can't be used.
|
||||
using ConnectedDevicePorts = std::map<int32_t, std::vector<int32_t>>;
|
||||
using ConnectedDevicePorts = std::map<int32_t, std::set<int32_t>>;
|
||||
// Maps port ids and port config ids to patch ids.
|
||||
// Multimap because both ports and configs can be used by multiple patches.
|
||||
using Patches = std::multimap<int32_t, int32_t>;
|
||||
|
|
|
@ -26,7 +26,10 @@ cc_defaults {
|
|||
"libaudioaidlcommon",
|
||||
"libaidlcommonsupport",
|
||||
],
|
||||
header_libs: ["libaudioaidl_headers"],
|
||||
header_libs: [
|
||||
"libaudioaidl_headers",
|
||||
"libexpectedutils_headers",
|
||||
],
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <aidl/android/media/audio/common/AudioInputFlags.h>
|
||||
#include <aidl/android/media/audio/common/AudioIoFlags.h>
|
||||
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
|
||||
#include <error/expected_utils.h>
|
||||
|
||||
#include "ModuleConfig.h"
|
||||
|
||||
|
@ -499,18 +500,13 @@ std::vector<AudioPortConfig> 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;
|
||||
ndk::ScopedAStatus ModuleConfig::onExternalDeviceConnected(IModule* module, const AudioPort& port) {
|
||||
RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
|
||||
RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));
|
||||
|
||||
// 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;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
|
||||
if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
|
||||
|
@ -518,23 +514,20 @@ const ndk::ScopedAStatus& ModuleConfig::onExternalDeviceConnected(IModule* modul
|
|||
} else {
|
||||
mConnectedExternalSinkDevicePorts.insert(port.id);
|
||||
}
|
||||
return mStatus;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
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;
|
||||
ndk::ScopedAStatus ModuleConfig::onExternalDeviceDisconnected(IModule* module,
|
||||
const AudioPort& port) {
|
||||
RETURN_STATUS_IF_ERROR(module->getAudioPorts(&mPorts));
|
||||
RETURN_STATUS_IF_ERROR(module->getAudioRoutes(&mRoutes));
|
||||
|
||||
if (port.flags.getTag() == aidl::android::media::audio::common::AudioIoFlags::Tag::input) {
|
||||
mConnectedExternalSourceDevicePorts.erase(port.id);
|
||||
} else {
|
||||
mConnectedExternalSinkDevicePorts.erase(port.id);
|
||||
}
|
||||
return mStatus;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
bool ModuleConfig::isMmapSupported() const {
|
||||
|
|
|
@ -157,10 +157,10 @@ class ModuleConfig {
|
|||
return *config.begin();
|
||||
}
|
||||
|
||||
const ndk::ScopedAStatus& onExternalDeviceConnected(
|
||||
ndk::ScopedAStatus onExternalDeviceConnected(
|
||||
aidl::android::hardware::audio::core::IModule* module,
|
||||
const aidl::android::media::audio::common::AudioPort& port);
|
||||
const ndk::ScopedAStatus& onExternalDeviceDisconnected(
|
||||
ndk::ScopedAStatus onExternalDeviceDisconnected(
|
||||
aidl::android::hardware::audio::core::IModule* module,
|
||||
const aidl::android::media::audio::common::AudioPort& port);
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include <aidl/android/media/audio/common/AudioOutputFlags.h>
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <android/binder_enums.h>
|
||||
#include <error/expected_utils.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
|
||||
#include "AudioHalBinderServiceUtil.h"
|
||||
|
@ -412,9 +413,21 @@ class AudioCoreModuleBase {
|
|||
|
||||
void SetUpImpl(const std::string& moduleName) {
|
||||
ASSERT_NO_FATAL_FAILURE(ConnectToService(moduleName));
|
||||
ASSERT_IS_OK(module->getAudioPorts(&initialPorts));
|
||||
ASSERT_IS_OK(module->getAudioRoutes(&initialRoutes));
|
||||
}
|
||||
|
||||
void TearDownImpl() { debug.reset(); }
|
||||
void TearDownImpl() {
|
||||
debug.reset();
|
||||
std::vector<AudioPort> finalPorts;
|
||||
ASSERT_IS_OK(module->getAudioPorts(&finalPorts));
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual<AudioPort>(initialPorts, finalPorts))
|
||||
<< "The list of audio ports was not restored to the initial state";
|
||||
std::vector<AudioRoute> finalRoutes;
|
||||
ASSERT_IS_OK(module->getAudioRoutes(&finalRoutes));
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual<AudioRoute>(initialRoutes, finalRoutes))
|
||||
<< "The list of audio routes was not restored to the initial state";
|
||||
}
|
||||
|
||||
void ConnectToService(const std::string& moduleName) {
|
||||
ASSERT_EQ(module, nullptr);
|
||||
|
@ -502,17 +515,24 @@ class AudioCoreModuleBase {
|
|||
}
|
||||
}
|
||||
|
||||
// Warning: modifies the vectors!
|
||||
template <typename T>
|
||||
void VerifyVectorsAreEqual(std::vector<T>& v1, std::vector<T>& v2) {
|
||||
ASSERT_EQ(v1.size(), v2.size());
|
||||
std::sort(v1.begin(), v1.end());
|
||||
std::sort(v2.begin(), v2.end());
|
||||
if (v1 != v2) {
|
||||
FAIL() << "Vectors are not equal: v1 = " << ::android::internal::ToString(v1)
|
||||
<< ", v2 = " << ::android::internal::ToString(v2);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<IModule> module;
|
||||
std::unique_ptr<ModuleConfig> moduleConfig;
|
||||
AudioHalBinderServiceUtil binderUtil;
|
||||
std::unique_ptr<WithDebugFlags> debug;
|
||||
};
|
||||
|
||||
class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); }
|
||||
|
||||
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
|
||||
std::vector<AudioPort> initialPorts;
|
||||
std::vector<AudioRoute> initialRoutes;
|
||||
};
|
||||
|
||||
class WithDevicePortConnectedState {
|
||||
|
@ -530,16 +550,19 @@ class WithDevicePortConnectedState {
|
|||
<< "when external device disconnected";
|
||||
}
|
||||
}
|
||||
ScopedAStatus SetUpNoChecks(IModule* module, ModuleConfig* moduleConfig) {
|
||||
RETURN_STATUS_IF_ERROR(module->connectExternalDevice(mIdAndData, &mConnectedPort));
|
||||
RETURN_STATUS_IF_ERROR(moduleConfig->onExternalDeviceConnected(module, mConnectedPort));
|
||||
mModule = module;
|
||||
mModuleConfig = moduleConfig;
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
void SetUp(IModule* module, ModuleConfig* moduleConfig) {
|
||||
ASSERT_IS_OK(module->connectExternalDevice(mIdAndData, &mConnectedPort))
|
||||
ASSERT_NE(moduleConfig, nullptr);
|
||||
ASSERT_IS_OK(SetUpNoChecks(module, moduleConfig))
|
||||
<< "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; }
|
||||
|
@ -551,6 +574,13 @@ class WithDevicePortConnectedState {
|
|||
AudioPort mConnectedPort;
|
||||
};
|
||||
|
||||
class AudioCoreModule : public AudioCoreModuleBase, public testing::TestWithParam<std::string> {
|
||||
public:
|
||||
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpImpl(GetParam())); }
|
||||
|
||||
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownImpl()); }
|
||||
};
|
||||
|
||||
class StreamContext {
|
||||
public:
|
||||
typedef AidlMessageQueue<StreamDescriptor::Command, SynchronizedReadWrite> CommandMQ;
|
||||
|
@ -1258,11 +1288,8 @@ TEST_P(AudioCoreModule, GetAudioPortsIsStable) {
|
|||
ASSERT_IS_OK(module->getAudioPorts(&ports1));
|
||||
std::vector<AudioPort> ports2;
|
||||
ASSERT_IS_OK(module->getAudioPorts(&ports2));
|
||||
ASSERT_EQ(ports1.size(), ports2.size())
|
||||
<< "Sizes of audio port arrays do not match across consequent calls to getAudioPorts";
|
||||
std::sort(ports1.begin(), ports1.end());
|
||||
std::sort(ports2.begin(), ports2.end());
|
||||
EXPECT_EQ(ports1, ports2);
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual<AudioPort>(ports1, ports2))
|
||||
<< "Audio port arrays do not match across consequent calls to getAudioPorts";
|
||||
}
|
||||
|
||||
TEST_P(AudioCoreModule, GetAudioRoutesIsStable) {
|
||||
|
@ -1270,11 +1297,8 @@ TEST_P(AudioCoreModule, GetAudioRoutesIsStable) {
|
|||
ASSERT_IS_OK(module->getAudioRoutes(&routes1));
|
||||
std::vector<AudioRoute> routes2;
|
||||
ASSERT_IS_OK(module->getAudioRoutes(&routes2));
|
||||
ASSERT_EQ(routes1.size(), routes2.size())
|
||||
<< "Sizes of audio route arrays do not match across consequent calls to getAudioRoutes";
|
||||
std::sort(routes1.begin(), routes1.end());
|
||||
std::sort(routes2.begin(), routes2.end());
|
||||
EXPECT_EQ(routes1, routes2);
|
||||
EXPECT_NO_FATAL_FAILURE(VerifyVectorsAreEqual<AudioRoute>(routes1, routes2))
|
||||
<< " Audio route arrays do not match across consequent calls to getAudioRoutes";
|
||||
}
|
||||
|
||||
TEST_P(AudioCoreModule, GetAudioRoutesAreValid) {
|
||||
|
@ -1792,39 +1816,151 @@ TEST_P(AudioCoreModule, ExternalDevicePortRoutes) {
|
|||
}
|
||||
}
|
||||
|
||||
class RoutedPortsProfilesSnapshot {
|
||||
public:
|
||||
explicit RoutedPortsProfilesSnapshot(int32_t portId) : mPortId(portId) {}
|
||||
void Capture(IModule* module) {
|
||||
std::vector<AudioRoute> routes;
|
||||
ASSERT_IS_OK(module->getAudioRoutesForAudioPort(mPortId, &routes));
|
||||
std::vector<AudioPort> allPorts;
|
||||
ASSERT_IS_OK(module->getAudioPorts(&allPorts));
|
||||
ASSERT_NO_FATAL_FAILURE(GetAllRoutedPorts(routes, allPorts));
|
||||
ASSERT_NO_FATAL_FAILURE(GetProfileSizes());
|
||||
}
|
||||
void VerifyNoProfilesChanges(const RoutedPortsProfilesSnapshot& before) {
|
||||
for (const auto& p : before.mRoutedPorts) {
|
||||
auto beforeIt = before.mPortProfileSizes.find(p.id);
|
||||
ASSERT_NE(beforeIt, before.mPortProfileSizes.end())
|
||||
<< "port ID " << p.id << " not found in the initial profile sizes";
|
||||
EXPECT_EQ(beforeIt->second, mPortProfileSizes[p.id])
|
||||
<< " port " << p.toString() << " has an unexpected profile size change"
|
||||
<< " following an external device connection and disconnection";
|
||||
}
|
||||
}
|
||||
void VerifyProfilesNonEmpty() {
|
||||
for (const auto& p : mRoutedPorts) {
|
||||
EXPECT_NE(0UL, mPortProfileSizes[p.id])
|
||||
<< " port " << p.toString() << " must have had its profiles"
|
||||
<< " populated while having a connected external device";
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<AudioPort>& getRoutedPorts() const { return mRoutedPorts; }
|
||||
|
||||
private:
|
||||
void GetAllRoutedPorts(const std::vector<AudioRoute>& routes,
|
||||
std::vector<AudioPort>& allPorts) {
|
||||
for (const auto& r : routes) {
|
||||
if (r.sinkPortId == mPortId) {
|
||||
for (const auto& srcPortId : r.sourcePortIds) {
|
||||
const auto srcPortIt = findById(allPorts, srcPortId);
|
||||
ASSERT_NE(allPorts.end(), srcPortIt) << "port ID " << srcPortId;
|
||||
mRoutedPorts.push_back(*srcPortIt);
|
||||
}
|
||||
} else {
|
||||
const auto sinkPortIt = findById(allPorts, r.sinkPortId);
|
||||
ASSERT_NE(allPorts.end(), sinkPortIt) << "port ID " << r.sinkPortId;
|
||||
mRoutedPorts.push_back(*sinkPortIt);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GetProfileSizes() {
|
||||
std::transform(
|
||||
mRoutedPorts.begin(), mRoutedPorts.end(),
|
||||
std::inserter(mPortProfileSizes, mPortProfileSizes.end()),
|
||||
[](const auto& port) { return std::make_pair(port.id, port.profiles.size()); });
|
||||
}
|
||||
|
||||
const int32_t mPortId;
|
||||
std::vector<AudioPort> mRoutedPorts;
|
||||
std::map<int32_t, size_t> mPortProfileSizes;
|
||||
};
|
||||
|
||||
// Note: This test relies on simulation of external device connections by the HAL module.
|
||||
TEST_P(AudioCoreModule, ExternalDeviceMixPortConfigs) {
|
||||
// After an external device has been connected, all mix ports that can be routed
|
||||
// to the device port for the connected device must have non-empty profiles.
|
||||
// Since the test connects and disconnects a single device each time, the size
|
||||
// of profiles for all mix ports routed to the device port under test must get back
|
||||
// to the original count once the external device is disconnected.
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
|
||||
std::vector<AudioPort> externalDevicePorts = moduleConfig->getExternalDevicePorts();
|
||||
if (externalDevicePorts.empty()) {
|
||||
GTEST_SKIP() << "No external devices in the module.";
|
||||
}
|
||||
for (const auto& port : externalDevicePorts) {
|
||||
WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port));
|
||||
ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get()));
|
||||
std::vector<AudioRoute> routes;
|
||||
ASSERT_IS_OK(module->getAudioRoutesForAudioPort(portConnected.getId(), &routes));
|
||||
std::vector<AudioPort> allPorts;
|
||||
ASSERT_IS_OK(module->getAudioPorts(&allPorts));
|
||||
for (const auto& r : routes) {
|
||||
if (r.sinkPortId == portConnected.getId()) {
|
||||
for (const auto& srcPortId : r.sourcePortIds) {
|
||||
const auto srcPortIt = findById(allPorts, srcPortId);
|
||||
ASSERT_NE(allPorts.end(), srcPortIt) << "port ID " << srcPortId;
|
||||
EXPECT_NE(0UL, srcPortIt->profiles.size())
|
||||
<< " source port " << srcPortIt->toString() << " must have its profiles"
|
||||
<< " populated following external device connection";
|
||||
}
|
||||
} else {
|
||||
const auto sinkPortIt = findById(allPorts, r.sinkPortId);
|
||||
ASSERT_NE(allPorts.end(), sinkPortIt) << "port ID " << r.sinkPortId;
|
||||
EXPECT_NE(0UL, sinkPortIt->profiles.size())
|
||||
<< " source port " << sinkPortIt->toString() << " must have its"
|
||||
<< " profiles populated following external device connection";
|
||||
SCOPED_TRACE(port.toString());
|
||||
RoutedPortsProfilesSnapshot before(port.id);
|
||||
ASSERT_NO_FATAL_FAILURE(before.Capture(module.get()));
|
||||
if (before.getRoutedPorts().empty()) continue;
|
||||
{
|
||||
WithDevicePortConnectedState portConnected(GenerateUniqueDeviceAddress(port));
|
||||
ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get(), moduleConfig.get()));
|
||||
RoutedPortsProfilesSnapshot connected(portConnected.getId());
|
||||
ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get()));
|
||||
EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty());
|
||||
}
|
||||
RoutedPortsProfilesSnapshot after(port.id);
|
||||
ASSERT_NO_FATAL_FAILURE(after.Capture(module.get()));
|
||||
EXPECT_NO_FATAL_FAILURE(after.VerifyNoProfilesChanges(before));
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This test relies on simulation of external device connections by the HAL module.
|
||||
TEST_P(AudioCoreModule, TwoExternalDevicesMixPortConfigsNested) {
|
||||
// Ensure that in the case when two external devices are connected to the same
|
||||
// device port, disconnecting one of them does not erase the profiles of routed mix ports.
|
||||
// In this scenario, the connections are "nested."
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
|
||||
std::vector<AudioPort> externalDevicePorts = moduleConfig->getExternalDevicePorts();
|
||||
if (externalDevicePorts.empty()) {
|
||||
GTEST_SKIP() << "No external devices in the module.";
|
||||
}
|
||||
for (const auto& port : externalDevicePorts) {
|
||||
SCOPED_TRACE(port.toString());
|
||||
WithDevicePortConnectedState portConnected1(GenerateUniqueDeviceAddress(port));
|
||||
ASSERT_NO_FATAL_FAILURE(portConnected1.SetUp(module.get(), moduleConfig.get()));
|
||||
{
|
||||
// Connect and disconnect another device, if possible. It might not be possible
|
||||
// for point-to-point connections, like analog or SPDIF.
|
||||
WithDevicePortConnectedState portConnected2(GenerateUniqueDeviceAddress(port));
|
||||
if (auto status = portConnected2.SetUpNoChecks(module.get(), moduleConfig.get());
|
||||
!status.isOk()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RoutedPortsProfilesSnapshot connected(portConnected1.getId());
|
||||
ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get()));
|
||||
EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This test relies on simulation of external device connections by the HAL module.
|
||||
TEST_P(AudioCoreModule, TwoExternalDevicesMixPortConfigsInterleaved) {
|
||||
// Ensure that in the case when two external devices are connected to the same
|
||||
// device port, disconnecting one of them does not erase the profiles of routed mix ports.
|
||||
// In this scenario, the connections are "interleaved."
|
||||
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
|
||||
std::vector<AudioPort> externalDevicePorts = moduleConfig->getExternalDevicePorts();
|
||||
if (externalDevicePorts.empty()) {
|
||||
GTEST_SKIP() << "No external devices in the module.";
|
||||
}
|
||||
for (const auto& port : externalDevicePorts) {
|
||||
SCOPED_TRACE(port.toString());
|
||||
auto portConnected1 =
|
||||
std::make_unique<WithDevicePortConnectedState>(GenerateUniqueDeviceAddress(port));
|
||||
ASSERT_NO_FATAL_FAILURE(portConnected1->SetUp(module.get(), moduleConfig.get()));
|
||||
WithDevicePortConnectedState portConnected2(GenerateUniqueDeviceAddress(port));
|
||||
// Connect another device, if possible. It might not be possible for point-to-point
|
||||
// connections, like analog or SPDIF.
|
||||
if (auto status = portConnected2.SetUpNoChecks(module.get(), moduleConfig.get());
|
||||
!status.isOk()) {
|
||||
continue;
|
||||
}
|
||||
portConnected1.reset();
|
||||
RoutedPortsProfilesSnapshot connected(portConnected2.getId());
|
||||
ASSERT_NO_FATAL_FAILURE(connected.Capture(module.get()));
|
||||
EXPECT_NO_FATAL_FAILURE(connected.VerifyProfilesNonEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue