M module-audio/Audio/Audio.hpp => module-audio/Audio/Audio.hpp +6 -4
@@ 90,12 90,12 @@ namespace audio
audio::Profile::Type GetPriorityPlaybackProfile() const
{
- if (audioSinkState.isConnected(EventType::BlutoothA2DPDeviceState)) {
- return Profile::Type::PlaybackBluetoothA2DP;
- }
if (audioSinkState.isConnected(EventType::JackState)) {
return Profile::Type::PlaybackHeadphones;
}
+ if (audioSinkState.isConnected(EventType::BlutoothA2DPDeviceState)) {
+ return Profile::Type::PlaybackBluetoothA2DP;
+ }
return Profile::Type::PlaybackLoudspeaker;
}
@@ 111,6 111,9 @@ namespace audio
virtual audio::RetCode Resume();
virtual audio::RetCode Mute();
+ protected:
+ AudioSinkState audioSinkState;
+
private:
void SendUpdateEventsToCurrentOperation();
/**
@@ 126,7 129,6 @@ namespace audio
void UpdateProfiles(audio::PlaybackType playbackType);
Muted muted = Muted::False;
- AudioSinkState audioSinkState;
std::shared_ptr<BluetoothStreamData> btData;
M module-audio/Audio/Operation/Operation.cpp => module-audio/Audio/Operation/Operation.cpp +10 -2
@@ 55,13 55,21 @@ namespace audio
}
}
- audio::RetCode Operation::SwitchToPriorityProfile()
+ std::optional<Profile::Type> Operation::GetPriorityProfile() const
{
for (auto &p : supportedProfiles) {
if (p.isAvailable == true) {
- return SwitchProfile(p.profile->GetType());
+ return p.profile->GetType();
}
}
+ return {};
+ }
+
+ audio::RetCode Operation::SwitchToPriorityProfile()
+ {
+ if (const auto priorityProfile = GetPriorityProfile(); priorityProfile.has_value()) {
+ return SwitchProfile(priorityProfile.value());
+ }
return audio::RetCode::ProfileNotSet;
}
M module-audio/Audio/Operation/Operation.hpp => module-audio/Audio/Operation/Operation.hpp +2 -0
@@ 5,6 5,7 @@
#include <memory>
#include <functional>
+#include <optional>
#include <Audio/AudioCommon.hpp>
#include <Audio/AudioDeviceFactory.hpp>
@@ 114,6 115,7 @@ namespace audio
{
return filePath;
}
+ std::optional<Profile::Type> GetPriorityProfile() const;
/**
* @brief Switches operation to priority profile.
*/
M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +1 -1
@@ 21,8 21,8 @@ namespace audio
: Operation(callback, playbackType), dec(nullptr)
{
// order defines priority
- AddProfile(Profile::Type::PlaybackBluetoothA2DP, playbackType, false);
AddProfile(Profile::Type::PlaybackHeadphones, playbackType, false);
+ AddProfile(Profile::Type::PlaybackBluetoothA2DP, playbackType, false);
AddProfile(Profile::Type::PlaybackLoudspeaker, playbackType, true);
endOfFileCallback = [this]() {
M module-audio/Audio/Operation/RecorderOperation.cpp => module-audio/Audio/Operation/RecorderOperation.cpp +1 -1
@@ 44,8 44,8 @@ namespace audio
};
// order defines priority
- AddProfile(Profile::Type::RecordingBluetoothHSP, PlaybackType::None, false);
AddProfile(Profile::Type::RecordingHeadphones, PlaybackType::None, false);
+ AddProfile(Profile::Type::RecordingBluetoothHSP, PlaybackType::None, false);
AddProfile(Profile::Type::RecordingBuiltInMic, PlaybackType::None, true);
auto defaultProfile = GetProfile(Profile::Type::PlaybackLoudspeaker);
M module-audio/Audio/Operation/RouterOperation.cpp => module-audio/Audio/Operation/RouterOperation.cpp +1 -1
@@ 23,8 23,8 @@ namespace audio
: Operation(callback)
{
// order defines priority
- AddProfile(Profile::Type::RoutingBluetoothHSP, PlaybackType::None, false);
AddProfile(Profile::Type::RoutingHeadphones, PlaybackType::None, false);
+ AddProfile(Profile::Type::RoutingBluetoothHSP, PlaybackType::None, false);
AddProfile(Profile::Type::RoutingEarspeaker, PlaybackType::None, true);
AddProfile(Profile::Type::RoutingLoudspeaker, PlaybackType::None, true);
}
M module-audio/Audio/test/unittest_audio.cpp => module-audio/Audio/test/unittest_audio.cpp +213 -0
@@ 16,6 16,7 @@
#include "Audio/AudioMux.hpp"
#include "Audio/Audio.hpp"
#include "Audio/Operation/Operation.hpp"
+#include <Audio/Operation/RouterOperation.hpp>
using namespace audio;
@@ 138,11 139,57 @@ class MockAudio : public audio::Audio
return state;
}
+ void setConnected(EventType deviceUpdateEvent)
+ {
+ audioSinkState.setConnected(deviceUpdateEvent, true);
+ }
+
+ void setDisconnected(EventType deviceUpdateEvent)
+ {
+ audioSinkState.setConnected(deviceUpdateEvent, false);
+ }
+
State state = State::Idle;
audio::PlaybackType plbckType;
audio::Operation::State opState;
};
+class MockRouterOperation : public RouterOperation
+{
+ bool loudspeakerEnabled = false;
+
+ bool isLoudspeakerOn() const
+ {
+ return std::find_if(supportedProfiles.begin(), supportedProfiles.end(), [this](const SupportedProfile &s) {
+ return (s.profile->GetType() == Profile::Type::RoutingLoudspeaker) && s.isAvailable &&
+ loudspeakerEnabled;
+ }) != std::end(supportedProfiles);
+ }
+
+ public:
+ /* value - volume and gain value */
+ explicit MockRouterOperation(AudioServiceMessage::Callback callback) : RouterOperation(nullptr, callback)
+ {}
+
+ std::optional<Profile::Type> getPriorityProfile() const
+ {
+ if (isLoudspeakerOn()) {
+ return Profile::Type::RoutingLoudspeaker;
+ }
+ return Operation::GetPriorityProfile();
+ }
+
+ void enableLoudspeaker() noexcept
+ {
+ loudspeakerEnabled = true;
+ }
+
+ void disableLoudspeaker() noexcept
+ {
+ loudspeakerEnabled = false;
+ }
+};
+
TEST_CASE("Test AudioMux")
{
using namespace audio;
@@ 451,3 498,169 @@ TEST_CASE("Test AudioMux")
}
}
}
+
+SCENARIO("Profile playback priorities tests")
+{
+ GIVEN("Audio idle instance")
+ {
+ MockAudio audio{Audio::State::Idle, PlaybackType::None, Operation::State::Idle};
+ WHEN("All audio devices are connected")
+ {
+ audio.setConnected(EventType::JackState);
+ audio.setConnected(EventType::BlutoothA2DPDeviceState);
+ THEN("Headphones are prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackHeadphones);
+ }
+ AND_WHEN("Bluetooth is disconnected")
+ {
+ audio.setDisconnected(EventType::BlutoothA2DPDeviceState);
+ THEN("Headphones are prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackHeadphones);
+ }
+ }
+ AND_WHEN("Headphones are disconnected")
+ {
+ audio.setDisconnected(EventType::JackState);
+ THEN("Bluetooth is prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackBluetoothA2DP);
+ }
+ }
+ }
+ WHEN("Only bluetooth device is connected")
+ {
+ audio.setConnected(EventType::BlutoothA2DPDeviceState);
+ THEN("Bluetooth is prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackBluetoothA2DP);
+ }
+ }
+ WHEN("Only headphones are connected")
+ {
+ audio.setConnected(EventType::JackState);
+ THEN("Headphones are prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackHeadphones);
+ }
+ }
+ WHEN("All audio devices are disconnected")
+ {
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(audio.GetPriorityPlaybackProfile() == Profile::Type::PlaybackLoudspeaker);
+ }
+ }
+ }
+}
+
+SCENARIO("Router playback priorities tests")
+{
+ GIVEN("Router operation instance")
+ {
+ MockRouterOperation routerOperation([](const sys::Message *e) { return "1"; });
+ WHEN("All audio devices are disconnected, loudspeaker is off")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOff));
+ THEN("Earspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingEarspeaker);
+ }
+ AND_WHEN("Loudspeaker is on")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOn));
+ routerOperation.enableLoudspeaker();
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingLoudspeaker);
+ }
+ }
+ }
+
+ WHEN("All audio devices are connected, loudspeaker is off")
+ {
+ routerOperation.SendEvent(
+ std::make_shared<Event>(audio::EventType::BlutoothHSPDeviceState, Event::DeviceState::Connected));
+ routerOperation.SendEvent(
+ std::make_shared<Event>(audio::EventType::JackState, Event::DeviceState::Connected));
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOff));
+
+ THEN("Headphones are prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingHeadphones);
+ }
+ AND_WHEN("Loudspeaker is on")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOn));
+ routerOperation.enableLoudspeaker();
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingLoudspeaker);
+ }
+ }
+
+ WHEN("Headphones are disconnected")
+ {
+ routerOperation.SendEvent(
+ std::make_shared<Event>(audio::EventType::JackState, Event::DeviceState::Disconnected));
+
+ THEN("Bluetooth HSP is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingBluetoothHSP);
+ }
+ AND_WHEN("Loudspeaker is on")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOn));
+ routerOperation.enableLoudspeaker();
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingLoudspeaker);
+ }
+ }
+ }
+ }
+
+ WHEN("Only bluetooth HSP is connected, loudspeaker is off")
+ {
+ routerOperation.SendEvent(
+ std::make_shared<Event>(audio::EventType::BlutoothHSPDeviceState, Event::DeviceState::Connected));
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOff));
+
+ THEN("Bluetooth HSP is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingBluetoothHSP);
+ }
+ AND_WHEN("Loudspeaker is on")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOn));
+ routerOperation.enableLoudspeaker();
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingLoudspeaker);
+ }
+ }
+ }
+
+ WHEN("Only headphones are connected, loudspeaker is off")
+ {
+ routerOperation.SendEvent(
+ std::make_shared<Event>(audio::EventType::JackState, Event::DeviceState::Connected));
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOff));
+
+ THEN("Headphones are prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingHeadphones);
+ }
+ AND_WHEN("Loudspeaker is on")
+ {
+ routerOperation.SendEvent(std::make_shared<Event>(audio::EventType::CallLoudspeakerOn));
+ routerOperation.enableLoudspeaker();
+ THEN("Loudspeaker is prioritized")
+ {
+ REQUIRE(routerOperation.getPriorityProfile() == Profile::Type::RoutingLoudspeaker);
+ }
+ }
+ }
+ }
+}
M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +2 -1
@@ 341,7 341,8 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
if (opType == Operation::Type::Playback) {
auto input = audioMux.GetPlaybackInput(playbackType);
- if (playbackType == audio::PlaybackType::CallRingtone && bluetoothHSPConnected) {
+ if (playbackType == audio::PlaybackType::CallRingtone && bluetoothHSPConnected && input &&
+ (*input)->audio->GetPriorityPlaybackProfile() == Profile::Type::PlaybackBluetoothA2DP) {
LOG_DEBUG("Sending Bluetooth start ringing");
bus.sendUnicast(std::make_shared<message::bluetooth::Ring>(message::bluetooth::Ring::State::Enable),
service::name::bluetooth);