audio HAL: Support for external device connections
Add methods 'IModule.connect/disconnectExternalDevice' which inform
audio HAL about connection / disconnection of an external
non-attached device. Add method 'getAudioRoutesForPort' to
retrieve only routes that include the specified port.
Update the behavior of 'getAudioPorts' and 'getAudioRoutes'
indicating that the result may change due to instantiation
of new device ports for connected external devices.
Clarify behavior of 'IModule.setAudioPortConfig' that it can not
work with device ports with no profiles.
Add debug flags structure 'ModuleDebug' and method
'IModule.setModuleDebug' to control the debugging aspects. VTS
tests use these flags to test HAL behavior which would otherwise
require human intervention.
Update the default implementation and VTS for the AIDL changes.
Bug: 205884982
Test: atest VtsHalAudioCoreTargetTest
Merged-In: Iad5f7009e283729206f88b6278c8992f7f8a92a2
Change-Id: Iad5f7009e283729206f88b6278c8992f7f8a92a2
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index 3faa39a..3f8d088 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -46,14 +46,16 @@
for (const auto& port : mPorts) {
if (port.ext.getTag() != AudioPortExt::Tag::device) continue;
const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
- const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input;
if (devicePort.device.type.connection.empty()) {
+ const bool isInput = port.flags.getTag() == AudioIoFlags::Tag::input;
// Permanently attached device.
if (isInput) {
mAttachedSourceDevicePorts.insert(port.id);
} else {
mAttachedSinkDevicePorts.insert(port.id);
}
+ } else if (port.profiles.empty()) {
+ mExternalDevicePorts.insert(port.id);
}
}
if (!mStatus.isOk()) return;
@@ -62,6 +64,22 @@
mStatus = module->getAudioPortConfigs(&mInitialConfigs);
}
+std::vector<AudioPort> ModuleConfig::getAttachedDevicePorts() const {
+ std::vector<AudioPort> result;
+ std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [&](const auto& port) {
+ return mAttachedSinkDevicePorts.count(port.id) != 0 ||
+ mAttachedSourceDevicePorts.count(port.id) != 0;
+ });
+ return result;
+}
+
+std::vector<AudioPort> ModuleConfig::getExternalDevicePorts() const {
+ std::vector<AudioPort> result;
+ std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result),
+ [&](const auto& port) { return mExternalDevicePorts.count(port.id) != 0; });
+ return result;
+}
+
std::vector<AudioPort> ModuleConfig::getInputMixPorts() const {
std::vector<AudioPort> result;
std::copy_if(mPorts.begin(), mPorts.end(), std::back_inserter(result), [](const auto& port) {
@@ -229,6 +247,23 @@
return result;
}
+std::string ModuleConfig::toString() const {
+ std::string result;
+ result.append("Ports: ");
+ result.append(android::internal::ToString(mPorts));
+ result.append("Initial configs: ");
+ result.append(android::internal::ToString(mInitialConfigs));
+ result.append("Attached sink device ports: ");
+ result.append(android::internal::ToString(mAttachedSinkDevicePorts));
+ result.append("Attached source device ports: ");
+ result.append(android::internal::ToString(mAttachedSourceDevicePorts));
+ result.append("External device ports: ");
+ result.append(android::internal::ToString(mExternalDevicePorts));
+ result.append("Routes: ");
+ result.append(android::internal::ToString(mRoutes));
+ return result;
+}
+
static std::vector<AudioPortConfig> combineAudioConfigs(const AudioPort& port,
const AudioProfile& profile) {
std::vector<AudioPortConfig> configs;
@@ -319,10 +354,14 @@
if (singleProfile && !result.empty()) return result;
}
if (resultSizeBefore == result.size()) {
- AudioPortConfig empty;
- empty.portId = devicePort.id;
- empty.ext = devicePort.ext;
- result.push_back(empty);
+ std::copy_if(mInitialConfigs.begin(), mInitialConfigs.end(), std::back_inserter(result),
+ [&](const auto& config) { return config.portId == devicePort.id; });
+ if (resultSizeBefore == result.size()) {
+ AudioPortConfig empty;
+ empty.portId = devicePort.id;
+ empty.ext = devicePort.ext;
+ result.push_back(empty);
+ }
}
if (singleProfile) return result;
}
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index 2e86b97..0e2738b 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -37,6 +37,8 @@
android::binder::Status getStatus() const { return mStatus; }
std::string getError() const { return mStatus.toString8().c_str(); }
+ std::vector<android::media::audio::common::AudioPort> getAttachedDevicePorts() const;
+ std::vector<android::media::audio::common::AudioPort> getExternalDevicePorts() const;
std::vector<android::media::audio::common::AudioPort> getInputMixPorts() const;
std::vector<android::media::audio::common::AudioPort> getOutputMixPorts() const;
std::vector<android::media::audio::common::AudioPort> getMixPorts(bool isInput) const {
@@ -59,6 +61,10 @@
std::optional<SrcSinkPair> getRoutableSrcSinkPair(bool isInput) const;
std::vector<SrcSinkGroup> getRoutableSrcSinkGroups(bool isInput) const;
+ std::vector<android::media::audio::common::AudioPortConfig>
+ getPortConfigsForAttachedDevicePorts() const {
+ return generateAudioDevicePortConfigs(getAttachedDevicePorts(), false);
+ }
std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForMixPorts() const {
auto inputs = generateInputAudioMixPortConfigs(getInputMixPorts(), false);
auto outputs = generateOutputAudioMixPortConfigs(getOutputMixPorts(), false);
@@ -98,15 +104,18 @@
}
}
+ std::vector<android::media::audio::common::AudioPortConfig> getPortConfigsForDevicePort(
+ const android::media::audio::common::AudioPort& port) const {
+ return generateAudioDevicePortConfigs({port}, false);
+ }
android::media::audio::common::AudioPortConfig getSingleConfigForDevicePort(
const android::media::audio::common::AudioPort& port) const {
- for (const auto& config : mInitialConfigs) {
- if (config.portId == port.id) return config;
- }
const auto config = generateAudioDevicePortConfigs({port}, true);
return *config.begin();
}
+ std::string toString() const;
+
private:
std::vector<android::media::audio::common::AudioPortConfig> generateInputAudioMixPortConfigs(
const std::vector<android::media::audio::common::AudioPort>& ports,
@@ -117,7 +126,8 @@
// Unlike MixPorts, the generator for DevicePorts always returns a non-empty
// vector for a non-empty input port list. If there are no profiles in the
- // port, a vector with an empty config is returned.
+ // port, its initial configs are looked up, if there are none,
+ // then an empty config is used, assuming further negotiation via setAudioPortConfig.
std::vector<android::media::audio::common::AudioPortConfig> generateAudioDevicePortConfigs(
const std::vector<android::media::audio::common::AudioPort>& ports,
bool singleProfile) const;
@@ -127,5 +137,6 @@
std::vector<android::media::audio::common::AudioPortConfig> mInitialConfigs;
std::set<int32_t> mAttachedSinkDevicePorts;
std::set<int32_t> mAttachedSourceDevicePorts;
+ std::set<int32_t> mExternalDevicePorts;
std::vector<android::hardware::audio::core::AudioRoute> mRoutes;
};
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index cadeb0c..824ac8d 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -22,6 +22,9 @@
#include <set>
#include <string>
+#define LOG_TAG "VtsHalAudioCore"
+#include <android-base/logging.h>
+
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/properties.h>
@@ -45,9 +48,12 @@
using android::hardware::audio::core::IModule;
using android::hardware::audio::core::IStreamIn;
using android::hardware::audio::core::IStreamOut;
+using android::hardware::audio::core::ModuleDebug;
using android::media::audio::common::AudioContentType;
using android::media::audio::common::AudioDevice;
+using android::media::audio::common::AudioDeviceAddress;
using android::media::audio::common::AudioDeviceType;
+using android::media::audio::common::AudioFormatType;
using android::media::audio::common::AudioIoFlags;
using android::media::audio::common::AudioOutputFlags;
using android::media::audio::common::AudioPort;
@@ -63,7 +69,7 @@
}
template <typename C>
-std::vector<int32_t> getNonExistentIds(const C& allIds) {
+std::vector<int32_t> GetNonExistentIds(const C& allIds) {
if (allIds.empty()) {
return std::vector<int32_t>{-1, 0, 1};
}
@@ -73,6 +79,12 @@
return nonExistentIds;
}
+AudioDeviceAddress GenerateUniqueDeviceAddress() {
+ static int nextId = 1;
+ // TODO: Use connection-specific ID.
+ return AudioDeviceAddress::make<AudioDeviceAddress::Tag::id>(std::to_string(++nextId));
+}
+
struct AidlDeathRecipient : IBinder::DeathRecipient {
std::mutex mutex;
std::condition_variable condition;
@@ -107,70 +119,28 @@
return false;
}
-class AudioCoreModule : public testing::TestWithParam<std::string> {
+class WithDebugFlags {
public:
- void SetUp() override { ASSERT_NO_FATAL_FAILURE(ConnectToService()); }
-
- void ConnectToService() {
- module = android::waitForDeclaredService<IModule>(String16(GetParam().c_str()));
- ASSERT_NE(module, nullptr);
- }
-
- void RestartService() {
- ASSERT_NE(module, nullptr);
- moduleConfig.reset();
- deathHandler = sp<AidlDeathRecipient>::make();
- ASSERT_EQ(NO_ERROR, IModule::asBinder(module)->linkToDeath(deathHandler));
- ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1"));
- EXPECT_TRUE(deathHandler->waitForFired(3000));
- deathHandler = nullptr;
- ASSERT_NO_FATAL_FAILURE(ConnectToService());
- }
-
- template <typename Entity>
- void GetAllEntityIds(std::set<int32_t>* entityIds,
- Status (IModule::*getter)(std::vector<Entity>*),
- const std::string& errorMessage) {
- std::vector<Entity> entities;
- {
- Status status = (module.get()->*getter)(&entities);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
- }
- std::transform(entities.begin(), entities.end(),
- std::inserter(*entityIds, entityIds->begin()),
- [](const auto& entity) { return entity.id; });
- EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage;
- }
-
- void GetAllPatchIds(std::set<int32_t>* patchIds) {
- return GetAllEntityIds<AudioPatch>(
- patchIds, &IModule::getAudioPatches,
- "IDs of audio patches returned by IModule.getAudioPatches are not unique");
- }
-
- void GetAllPortIds(std::set<int32_t>* portIds) {
- return GetAllEntityIds<AudioPort>(
- portIds, &IModule::getAudioPorts,
- "IDs of audio ports returned by IModule.getAudioPorts are not unique");
- }
-
- void GetAllPortConfigIds(std::set<int32_t>* portConfigIds) {
- return GetAllEntityIds<AudioPortConfig>(
- portConfigIds, &IModule::getAudioPortConfigs,
- "IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique");
- }
-
- void SetUpModuleConfig() {
- if (moduleConfig == nullptr) {
- moduleConfig = std::make_unique<ModuleConfig>(module.get());
- ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode())
- << "ModuleConfig init error: " << moduleConfig->getError();
+ WithDebugFlags() {}
+ explicit WithDebugFlags(const ModuleDebug& initial) : mInitial(initial), mFlags(initial) {}
+ explicit WithDebugFlags(const WithDebugFlags& initial)
+ : mInitial(initial.mFlags), mFlags(initial.mFlags) {}
+ ~WithDebugFlags() {
+ if (mModule != nullptr) {
+ Status status = mModule->setModuleDebug(mInitial);
+ EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
}
}
+ void SetUp(IModule* module) {
+ Status status = module->setModuleDebug(mFlags);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+ ModuleDebug& flags() { return mFlags; }
- sp<IModule> module;
- sp<AidlDeathRecipient> deathHandler;
- std::unique_ptr<ModuleConfig> moduleConfig;
+ private:
+ ModuleDebug mInitial;
+ ModuleDebug mFlags;
+ IModule* mModule = nullptr;
};
// For consistency, WithAudioPortConfig can start both with a non-existent
@@ -226,6 +196,147 @@
AudioPortConfig mConfig;
};
+class AudioCoreModule : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(ConnectToService());
+ debug.flags().simulateDeviceConnections = true;
+ ASSERT_NO_FATAL_FAILURE(debug.SetUp(module.get()));
+ }
+
+ void TearDown() override {
+ if (module != nullptr) {
+ Status status = module->setModuleDebug(ModuleDebug{});
+ EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
+ << status << " returned when resetting debug flags";
+ }
+ }
+
+ void ConnectToService() {
+ module = android::waitForDeclaredService<IModule>(String16(GetParam().c_str()));
+ ASSERT_NE(module, nullptr);
+ }
+
+ void RestartService() {
+ ASSERT_NE(module, nullptr);
+ moduleConfig.reset();
+ deathHandler = sp<AidlDeathRecipient>::make();
+ ASSERT_EQ(NO_ERROR, IModule::asBinder(module)->linkToDeath(deathHandler));
+ ASSERT_TRUE(base::SetProperty("sys.audio.restart.hal", "1"));
+ EXPECT_TRUE(deathHandler->waitForFired(3000));
+ deathHandler = nullptr;
+ ASSERT_NO_FATAL_FAILURE(ConnectToService());
+ }
+
+ void ApplyEveryConfig(const std::vector<AudioPortConfig>& configs) {
+ for (const auto& config : configs) {
+ ASSERT_NE(0, config.portId);
+ WithAudioPortConfig portConfig(config);
+ ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get())); // calls setAudioPortConfig
+ EXPECT_EQ(config.portId, portConfig.get().portId);
+ std::vector<AudioPortConfig> retrievedPortConfigs;
+ Status status = module->getAudioPortConfigs(&retrievedPortConfigs);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ const int32_t portConfigId = portConfig.getId();
+ auto configIt = std::find_if(
+ retrievedPortConfigs.begin(), retrievedPortConfigs.end(),
+ [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; });
+ EXPECT_NE(configIt, retrievedPortConfigs.end())
+ << "Port config id returned by setAudioPortConfig: " << portConfigId
+ << " is not found in the list returned by getAudioPortConfigs";
+ if (configIt != retrievedPortConfigs.end()) {
+ EXPECT_EQ(portConfig.get(), *configIt)
+ << "Applied port config returned by setAudioPortConfig: "
+ << portConfig.get().toString()
+ << " is not the same as retrieved via getAudioPortConfigs: "
+ << configIt->toString();
+ }
+ }
+ }
+
+ template <typename Entity>
+ void GetAllEntityIds(std::set<int32_t>* entityIds,
+ Status (IModule::*getter)(std::vector<Entity>*),
+ const std::string& errorMessage) {
+ std::vector<Entity> entities;
+ {
+ Status status = (module.get()->*getter)(&entities);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+ std::transform(entities.begin(), entities.end(),
+ std::inserter(*entityIds, entityIds->begin()),
+ [](const auto& entity) { return entity.id; });
+ EXPECT_EQ(entities.size(), entityIds->size()) << errorMessage;
+ }
+
+ void GetAllPatchIds(std::set<int32_t>* patchIds) {
+ return GetAllEntityIds<AudioPatch>(
+ patchIds, &IModule::getAudioPatches,
+ "IDs of audio patches returned by IModule.getAudioPatches are not unique");
+ }
+
+ void GetAllPortIds(std::set<int32_t>* portIds) {
+ return GetAllEntityIds<AudioPort>(
+ portIds, &IModule::getAudioPorts,
+ "IDs of audio ports returned by IModule.getAudioPorts are not unique");
+ }
+
+ void GetAllPortConfigIds(std::set<int32_t>* portConfigIds) {
+ return GetAllEntityIds<AudioPortConfig>(
+ portConfigIds, &IModule::getAudioPortConfigs,
+ "IDs of audio port configs returned by IModule.getAudioPortConfigs are not unique");
+ }
+
+ void SetUpModuleConfig() {
+ if (moduleConfig == nullptr) {
+ moduleConfig = std::make_unique<ModuleConfig>(module.get());
+ ASSERT_EQ(Status::EX_NONE, moduleConfig->getStatus().exceptionCode())
+ << "ModuleConfig init error: " << moduleConfig->getError();
+ }
+ }
+
+ sp<IModule> module;
+ sp<AidlDeathRecipient> deathHandler;
+ std::unique_ptr<ModuleConfig> moduleConfig;
+ WithDebugFlags debug;
+};
+
+class WithDevicePortConnectedState {
+ public:
+ explicit WithDevicePortConnectedState(const AudioPort& idAndData) : mIdAndData(idAndData) {}
+ WithDevicePortConnectedState(const AudioPort& id, const AudioDeviceAddress& address)
+ : mIdAndData(setAudioPortAddress(id, address)) {}
+ ~WithDevicePortConnectedState() {
+ if (mModule != nullptr) {
+ Status status = mModule->disconnectExternalDevice(getId());
+ EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
+ << status << " returned when disconnecting device port ID " << getId();
+ }
+ }
+ void SetUp(IModule* module) {
+ Status status = module->connectExternalDevice(mIdAndData, &mConnectedPort);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
+ << status << " returned 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";
+ mModule = module;
+ }
+ int32_t getId() const { return mConnectedPort.id; }
+ const AudioPort& get() { return mConnectedPort; }
+
+ private:
+ static AudioPort setAudioPortAddress(const AudioPort& id, const AudioDeviceAddress& address) {
+ AudioPort result = id;
+ result.ext.get<AudioPortExt::Tag::device>().device.address = address;
+ return result;
+ }
+
+ const AudioPort mIdAndData;
+ IModule* mModule = nullptr;
+ AudioPort mConnectedPort;
+};
+
template <typename Stream>
class WithStream {
public:
@@ -332,7 +443,7 @@
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
}
-TEST_P(AudioCoreModule, GetAudioPortsIsStatic) {
+TEST_P(AudioCoreModule, GetAudioPortsIsStable) {
std::vector<AudioPort> ports1;
{
Status status = module->getAudioPorts(&ports1);
@@ -344,13 +455,13 @@
ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
}
ASSERT_EQ(ports1.size(), ports2.size())
- << "Sizes of audio port arrays do not match across calls to getAudioPorts";
+ << "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);
}
-TEST_P(AudioCoreModule, GetAudioRoutesIsStatic) {
+TEST_P(AudioCoreModule, GetAudioRoutesIsStable) {
std::vector<AudioRoute> routes1;
{
Status status = module->getAudioRoutes(&routes1);
@@ -362,7 +473,7 @@
ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
}
ASSERT_EQ(routes1.size(), routes2.size())
- << "Sizes of audio route arrays do not match across calls to getAudioRoutes";
+ << "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);
@@ -401,6 +512,32 @@
}
}
+TEST_P(AudioCoreModule, GetAudioRoutesForAudioPort) {
+ std::set<int32_t> portIds;
+ ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
+ if (portIds.empty()) {
+ GTEST_SKIP() << "No ports in the module.";
+ }
+ for (const auto portId : portIds) {
+ std::vector<AudioRoute> routes;
+ Status status = module->getAudioRoutesForAudioPort(portId, &routes);
+ EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ for (const auto& r : routes) {
+ if (r.sinkPortId != portId) {
+ const auto& srcs = r.sourcePortIds;
+ EXPECT_TRUE(std::find(srcs.begin(), srcs.end(), portId) != srcs.end())
+ << " port ID " << portId << " does not used by the route " << r.toString();
+ }
+ }
+ }
+ for (const auto portId : GetNonExistentIds(portIds)) {
+ std::vector<AudioRoute> routes;
+ Status status = module->getAudioRoutesForAudioPort(portId, &routes);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for port ID " << portId;
+ }
+}
+
TEST_P(AudioCoreModule, CheckDevicePorts) {
std::vector<AudioPort> ports;
{
@@ -486,7 +623,7 @@
EXPECT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
EXPECT_EQ(portId, port.id);
}
- for (const auto portId : getNonExistentIds(portIds)) {
+ for (const auto portId : GetNonExistentIds(portIds)) {
AudioPort port;
Status status = module->getAudioPort(portId, &port);
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
@@ -494,10 +631,59 @@
}
}
+// Verify that HAL module reports for a connected device port at least one non-dynamic profile,
+// that is, a profile with actual supported configuration.
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, GetAudioPortWithExternalDevices) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ WithDevicePortConnectedState portConnected(portWithData);
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ const int32_t connectedPortId = portConnected.getId();
+ ASSERT_NE(portWithData.id, connectedPortId);
+ ASSERT_EQ(portWithData.ext.getTag(), portConnected.get().ext.getTag());
+ EXPECT_EQ(portWithData.ext.get<AudioPortExt::Tag::device>().device,
+ portConnected.get().ext.get<AudioPortExt::Tag::device>().device);
+ // Verify that 'getAudioPort' and 'getAudioPorts' return the same connected port.
+ AudioPort connectedPort;
+ Status status = module->getAudioPort(connectedPortId, &connectedPort);
+ EXPECT_EQ(Status::EX_NONE, status.exceptionCode())
+ << status << " returned for getAudioPort port ID " << connectedPortId;
+ EXPECT_EQ(portConnected.get(), connectedPort);
+ const auto& portProfiles = connectedPort.profiles;
+ EXPECT_NE(0, portProfiles.size())
+ << "Connected port has no profiles: " << connectedPort.toString();
+ const auto dynamicProfileIt =
+ std::find_if(portProfiles.begin(), portProfiles.end(), [](const auto& profile) {
+ return profile.format.type == AudioFormatType::DEFAULT;
+ });
+ EXPECT_EQ(portProfiles.end(), dynamicProfileIt) << "Connected port contains dynamic "
+ << "profiles: " << connectedPort.toString();
+
+ std::vector<AudioPort> allPorts;
+ {
+ Status status = module->getAudioPorts(&allPorts);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+ const auto allPortsIt = findById(allPorts, connectedPortId);
+ EXPECT_NE(allPorts.end(), allPortsIt);
+ if (allPortsIt != allPorts.end()) {
+ EXPECT_EQ(portConnected.get(), *allPortsIt);
+ }
+ }
+}
+
TEST_P(AudioCoreModule, OpenStreamInvalidPortConfigId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
{
sp<IStreamIn> stream;
Status status = module->openInputStream(portConfigId, {}, &stream);
@@ -537,7 +723,7 @@
TEST_P(AudioCoreModule, ResetAudioPortConfigInvalidId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
Status status = module->resetAudioPortConfig(portConfigId);
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
<< status << " returned for port config ID " << portConfigId;
@@ -608,39 +794,35 @@
EXPECT_EQ(suggestedConfig.flags.value(), appliedConfig.flags.value());
}
+TEST_P(AudioCoreModule, SetAllAttachedDevicePortConfigs) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForAttachedDevicePorts()));
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, SetAllExternalDevicePortConfigs) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ ASSERT_NO_FATAL_FAILURE(
+ ApplyEveryConfig(moduleConfig->getPortConfigsForDevicePort(portConnected.get())));
+ }
+}
+
TEST_P(AudioCoreModule, SetAllStaticAudioPortConfigs) {
ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
- const auto allPortConfigs = moduleConfig->getPortConfigsForMixPorts();
- for (const auto& config : allPortConfigs) {
- ASSERT_NE(0, config.portId);
- WithAudioPortConfig portConfig(config);
- ASSERT_NO_FATAL_FAILURE(portConfig.SetUp(module.get()));
- EXPECT_EQ(config.portId, portConfig.get().portId);
- std::vector<AudioPortConfig> retrievedPortConfigs;
- {
- Status status = module->getAudioPortConfigs(&retrievedPortConfigs);
- ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
- }
- const int32_t portConfigId = portConfig.getId();
- auto configIt = std::find_if(
- retrievedPortConfigs.begin(), retrievedPortConfigs.end(),
- [&portConfigId](const auto& retrConf) { return retrConf.id == portConfigId; });
- EXPECT_NE(configIt, retrievedPortConfigs.end())
- << "Port config id returned by setAudioPortConfig: " << portConfigId
- << " is not found in the list returned by getPortConfigsForMixPorts";
- if (configIt != retrievedPortConfigs.end()) {
- EXPECT_EQ(portConfig.get(), *configIt)
- << "Port config returned by getPortConfigsForMixPorts: " << configIt->toString()
- << " is not the same as returned by setAudioPortConfig: "
- << portConfig.get().toString();
- }
- }
+ ASSERT_NO_FATAL_FAILURE(ApplyEveryConfig(moduleConfig->getPortConfigsForMixPorts()));
}
TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortId) {
std::set<int32_t> portIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
- for (const auto portId : getNonExistentIds(portIds)) {
+ for (const auto portId : GetNonExistentIds(portIds)) {
AudioPortConfig portConfig, suggestedConfig;
bool applied;
portConfig.portId = portId;
@@ -656,7 +838,7 @@
TEST_P(AudioCoreModule, SetAudioPortConfigInvalidPortConfigId) {
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
AudioPortConfig portConfig, suggestedConfig;
bool applied;
portConfig.id = portConfigId;
@@ -669,6 +851,203 @@
}
}
+TEST_P(AudioCoreModule, TryConnectMissingDevice) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ AudioPort ignored;
+ WithDebugFlags doNotSimulateConnections(debug);
+ doNotSimulateConnections.flags().simulateDeviceConnections = false;
+ ASSERT_NO_FATAL_FAILURE(doNotSimulateConnections.SetUp(module.get()));
+ for (const auto& port : ports) {
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ Status status = module->connectExternalDevice(portWithData, &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ << status << " returned for static port " << portWithData.toString();
+ }
+}
+
+TEST_P(AudioCoreModule, TryChangingConnectionSimulationMidway) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ WithDevicePortConnectedState portConnected(*ports.begin(), GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ ModuleDebug midwayDebugChange = debug.flags();
+ midwayDebugChange.simulateDeviceConnections = false;
+ Status status = module->setModuleDebug(midwayDebugChange);
+ EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ << status << " returned when trying to disable connections simulation "
+ << "while having a connected device";
+}
+
+TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceInvalidPorts) {
+ AudioPort ignored;
+ std::set<int32_t> portIds;
+ ASSERT_NO_FATAL_FAILURE(GetAllPortIds(&portIds));
+ for (const auto portId : GetNonExistentIds(portIds)) {
+ AudioPort invalidPort;
+ invalidPort.id = portId;
+ Status status = module->connectExternalDevice(invalidPort, &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for port ID " << portId << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(portId);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for port ID " << portId
+ << " when setting DISCONNECTED state";
+ }
+
+ std::vector<AudioPort> ports;
+ {
+ Status status = module->getAudioPorts(&ports);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+ for (const auto& port : ports) {
+ if (port.ext.getTag() != AudioPortExt::Tag::device) {
+ Status status = module->connectExternalDevice(port, &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for non-device port ID " << port.id
+ << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for non-device port ID " << port.id
+ << " when setting DISCONNECTED state";
+ } else {
+ const auto& devicePort = port.ext.get<AudioPortExt::Tag::device>();
+ if (devicePort.device.type.connection.empty()) {
+ Status status = module->connectExternalDevice(port, &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for permanently attached device port ID " << port.id
+ << " when setting CONNECTED state";
+ status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned for permanently attached device port ID " << port.id
+ << " when setting DISCONNECTED state";
+ }
+ }
+ }
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, ConnectDisconnectExternalDeviceTwice) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ AudioPort ignored;
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ Status status = module->disconnectExternalDevice(port.id);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned when disconnecting already disconnected device port ID "
+ << port.id;
+ AudioPort portWithData = port;
+ portWithData.ext.get<AudioPortExt::Tag::device>().device.address =
+ GenerateUniqueDeviceAddress();
+ WithDevicePortConnectedState portConnected(portWithData);
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ status = module->connectExternalDevice(portConnected.get(), &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned when trying to connect a connected device port "
+ << portConnected.get().toString();
+ status = module->connectExternalDevice(portWithData, &ignored);
+ EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ << status << " returned when connecting again the external device "
+ << portWithData.ext.get<AudioPortExt::Tag::device>().device.toString();
+ if (status.exceptionCode() == Status::EX_NONE) {
+ ADD_FAILURE() << "Returned connected port " << ignored.toString() << " for template "
+ << portWithData.toString();
+ }
+ }
+}
+
+// Note: This test relies on simulation of external device connections by the HAL module.
+TEST_P(AudioCoreModule, DisconnectExternalDeviceNonResetPortConfig) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ const auto portConfig = moduleConfig->getSingleConfigForDevicePort(portConnected.get());
+ {
+ WithAudioPortConfig config(portConfig);
+ // Note: if SetUp fails, check the status of 'GetAudioPortWithExternalDevices' test.
+ // Our test assumes that 'getAudioPort' returns at least one profile, and it
+ // is not a dynamic profile.
+ ASSERT_NO_FATAL_FAILURE(config.SetUp(module.get()));
+ Status status = module->disconnectExternalDevice(portConnected.getId());
+ EXPECT_EQ(Status::EX_ILLEGAL_STATE, status.exceptionCode())
+ << status << " returned when trying to disconnect device port ID " << port.id
+ << " with active configuration " << config.getId();
+ }
+ }
+}
+
+TEST_P(AudioCoreModule, ExternalDevicePortRoutes) {
+ ASSERT_NO_FATAL_FAILURE(SetUpModuleConfig());
+ std::vector<AudioPort> ports = moduleConfig->getExternalDevicePorts();
+ if (ports.empty()) {
+ GTEST_SKIP() << "No external devices in the module.";
+ }
+ for (const auto& port : ports) {
+ std::vector<AudioRoute> routesBefore;
+ {
+ Status status = module->getAudioRoutes(&routesBefore);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+
+ int32_t connectedPortId;
+ {
+ WithDevicePortConnectedState portConnected(port, GenerateUniqueDeviceAddress());
+ ASSERT_NO_FATAL_FAILURE(portConnected.SetUp(module.get()));
+ connectedPortId = portConnected.getId();
+ std::vector<AudioRoute> connectedPortRoutes;
+ {
+ Status status =
+ module->getAudioRoutesForAudioPort(connectedPortId, &connectedPortRoutes);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode())
+ << status << " returned when retrieving routes for connected port id "
+ << connectedPortId;
+ }
+ // There must be routes for the port to be useful.
+ if (connectedPortRoutes.empty()) {
+ std::vector<AudioRoute> allRoutes;
+ Status status = module->getAudioRoutes(&allRoutes);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ ADD_FAILURE() << " no routes returned for the connected port "
+ << portConnected.get().toString()
+ << "; all routes: " << android::internal::ToString(allRoutes);
+ }
+ }
+ std::vector<AudioRoute> ignored;
+ Status status = module->getAudioRoutesForAudioPort(connectedPortId, &ignored);
+ ASSERT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
+ << status << " returned when retrieving routes for released connected port id "
+ << connectedPortId;
+
+ std::vector<AudioRoute> routesAfter;
+ {
+ Status status = module->getAudioRoutes(&routesAfter);
+ ASSERT_EQ(Status::EX_NONE, status.exceptionCode()) << status;
+ }
+ ASSERT_EQ(routesBefore.size(), routesAfter.size())
+ << "Sizes of audio route arrays do not match after creating and "
+ << "releasing a connected port";
+ std::sort(routesBefore.begin(), routesBefore.end());
+ std::sort(routesAfter.begin(), routesAfter.end());
+ EXPECT_EQ(routesBefore, routesAfter);
+ }
+}
+
template <typename Stream>
class AudioStream : public AudioCoreModule {
public:
@@ -899,7 +1278,7 @@
std::set<int32_t> portConfigIds;
ASSERT_NO_FATAL_FAILURE(GetAllPortConfigIds(&portConfigIds));
- for (const auto portConfigId : getNonExistentIds(portConfigIds)) {
+ for (const auto portConfigId : GetNonExistentIds(portConfigIds)) {
EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(
Status::EX_ILLEGAL_ARGUMENT, {portConfigId}, {sinkPortConfig.getId()}));
EXPECT_NO_FATAL_FAILURE(SetInvalidPatchHelper(Status::EX_ILLEGAL_ARGUMENT,
@@ -969,7 +1348,7 @@
// Then use the same patch setting, except for having an invalid ID.
std::set<int32_t> patchIds;
ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds));
- for (const auto patchId : getNonExistentIds(patchIds)) {
+ for (const auto patchId : GetNonExistentIds(patchIds)) {
AudioPatch patchWithNonExistendId = patch.get();
patchWithNonExistendId.id = patchId;
Status status = module->setAudioPatch(patchWithNonExistendId, &patchWithNonExistendId);
@@ -995,7 +1374,7 @@
TEST_P(AudioModulePatch, ResetInvalidPatchId) {
std::set<int32_t> patchIds;
ASSERT_NO_FATAL_FAILURE(GetAllPatchIds(&patchIds));
- for (const auto patchId : getNonExistentIds(patchIds)) {
+ for (const auto patchId : GetNonExistentIds(patchIds)) {
Status status = module->resetAudioPatch(patchId);
EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, status.exceptionCode())
<< status << " returned for patch ID " << patchId;