~aleteoryx/muditaos

689bd3069dd380df8bb07f9620ae948e485837b0 — Jakub Pyszczak 4 years ago 74ae1a6
[EGD-7206] Fix audio profiles priorities

Audio profiles priorities should be set as stated below:
1. Jack
2. Bluetooth
3. Earspeaker.
Added UT's to cover those requirements.
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);