~aleteoryx/muditaos

6be93bef430159995acbbcbcddb9009863532b5b — Jakub Pyszczak 4 years ago 55c41de
[EGD-6166] HSP ringing

Added HSP ring sound on incoming call event while
there's bluetooth connection active and HSP is
selected as the current profile.
Possibility of pick up/decline the call using
BT device buttons added.
Slightly changed HSP initialization according
to the UML-s added.
Changed call hangup handler in service cellular.
M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +6 -0
@@ 69,6 69,12 @@ namespace bluetooth
            return switchAudioProfile();
        case bluetooth::Command::None:
            return Error::Success;
        case Command::StartRinging:
            return profileManager->startRinging();
        case Command::StopRinging:
            return profileManager->stopRinging();
        case Command::StartRouting:
            return profileManager->initializeCall();
        case Command::StartStream:
            profileManager->start();
            return Error::Success;

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +3 -0
@@ 35,6 35,9 @@ namespace bluetooth
            PowerOff,
            Pair,
            Unpair,
            StartRinging,
            StopRinging,
            StartRouting,
            StartStream,
            StopStream,
            SwitchProfile,

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +15 -0
@@ 95,6 95,21 @@ namespace bluetooth
        pimpl->stop();
    }

    auto A2DP::startRinging() const noexcept -> Error::Code
    {
        return Error::SystemError;
    }

    auto A2DP::stopRinging() const noexcept -> Error::Code
    {
        return Error::SystemError;
    }

    auto A2DP::initializeCall() const noexcept -> Error::Code
    {
        return Error::SystemError;
    }

    void A2DP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        pimpl->setAudioDevice(std::move(audioDevice));

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +6 -0
@@ 30,6 30,12 @@ namespace bluetooth
        void disconnect() override;
        void start() override;
        void stop() override;
        /// @return SystemError - it's not posible to start ringing while there's A2DP active
        [[nodiscard]] auto startRinging() const noexcept -> Error::Code override;
        /// @return SystemError - it's not posible to stop ringing while there's A2DP active
        [[nodiscard]] auto stopRinging() const noexcept -> Error::Code override;
        /// @return SystemError - it's not posible to start routing while there's A2DP active
        [[nodiscard]] auto initializeCall() const noexcept -> Error::Code override;

        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override;


M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +59 -3
@@ 8,6 8,7 @@
#include <log/log.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-audio/AudioMessage.hpp>
#include <service-cellular/service-cellular/CellularServiceAPI.hpp>
#include <BluetoothWorker.hpp>

extern "C"


@@ 20,6 21,16 @@ extern "C"

namespace bluetooth
{
    bool CellularInterfaceImpl::answerIncomingCall(sys::Service *service)
    {
        return CellularServiceAPI::AnswerIncomingCall(service);
    }

    bool CellularInterfaceImpl::hangupCall(sys::Service *service)
    {
        return CellularServiceAPI::HangupCall(service);
    }

    HSP::HSP() : pimpl(std::make_unique<HSPImpl>(HSPImpl()))
    {}



@@ 71,6 82,24 @@ namespace bluetooth
        pimpl->stop();
    }

    auto HSP::startRinging() const noexcept -> Error::Code
    {
        pimpl->startRinging();
        return Error::Success;
    }

    auto HSP::stopRinging() const noexcept -> Error::Code
    {
        pimpl->stopRinging();
        return Error::Success;
    }

    auto HSP::initializeCall() const noexcept -> Error::Code
    {
        pimpl->initializeCall();
        return Error::Success;
    }

    HSP::~HSP() = default;

    uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;


@@ 78,6 107,7 @@ namespace bluetooth
    std::array<char, commandBufferLength> HSP::HSPImpl::ATcommandBuffer;
    std::array<uint8_t, serviceBufferLength> HSP::HSPImpl::serviceBuffer;
    std::unique_ptr<SCO> HSP::HSPImpl::sco;
    std::unique_ptr<CellularInterface> HSP::HSPImpl::cellularInterface = nullptr;
    const sys::Service *HSP::HSPImpl::ownerService;
    std::string HSP::HSPImpl::agServiceName = "PurePhone HSP";
    bool HSP::HSPImpl::isConnected          = false;


@@ 153,6 183,7 @@ namespace bluetooth
            else {
                scoHandle = hsp_subevent_audio_connection_complete_get_handle(event);
                LOG_DEBUG("Audio connection established with SCO handle 0x%04x.\n", scoHandle);
                cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
                hci_request_sco_can_send_now_event();
                RunLoop::trigger();
            }


@@ 160,6 191,7 @@ namespace bluetooth
        case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE:
            LOG_DEBUG("Audio connection released.\n\n");
            sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
            cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
            scoHandle   = HCI_CON_HANDLE_INVALID;
            isConnected = false;
            break;


@@ 185,12 217,20 @@ namespace bluetooth
        }
    }

    void HSP::HSPImpl::establishAudioConnection()
    {
        LOG_DEBUG("Establish Audio connection to %s...\n", bd_addr_to_str(deviceAddr));
        hsp_ag_establish_audio_connection();
    }

    auto HSP::HSPImpl::init() -> Error::Code
    {
        sco = std::make_unique<SCO>();
        sco->setOwnerService(ownerService);
        sco->init();

        cellularInterface = std::make_unique<CellularInterfaceImpl>();

        Profile::initL2cap();
        Profile::initSdp();



@@ 233,7 273,7 @@ namespace bluetooth
    void HSP::HSPImpl::setDeviceAddress(bd_addr_t addr)
    {
        bd_addr_copy(deviceAddr, addr);
        LOG_INFO("Address set!");
        LOG_DEBUG("Address set!");
    }

    void HSP::HSPImpl::setOwnerService(const sys::Service *service)


@@ 250,12 290,28 @@ namespace bluetooth
        if (!isConnected) {
            connect();
        }
        LOG_DEBUG("Establish Audio connection to %s...\n", bd_addr_to_str(deviceAddr));
        hsp_ag_establish_audio_connection();
    }
    void HSP::HSPImpl::stop()
    {
        stopRinging();
        hsp_ag_release_audio_connection();
    }

    void HSP::HSPImpl::startRinging() const noexcept
    {
        LOG_DEBUG("Bluetooth ring started");
        hsp_ag_start_ringing();
    }

    void HSP::HSPImpl::stopRinging() const noexcept
    {
        LOG_DEBUG("Bluetooth ring stopped");
        hsp_ag_stop_ringing();
    }

    void HSP::HSPImpl::initializeCall() const noexcept
    {
        stopRinging();
        establishAudioConnection();
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +24 -1
@@ 9,7 9,19 @@

namespace bluetooth
{

    class CellularInterface
    {
      public:
        virtual ~CellularInterface()                           = default;
        virtual bool answerIncomingCall(sys::Service *service) = 0;
        virtual bool hangupCall(sys::Service *service)         = 0;
    };
    class CellularInterfaceImpl : public CellularInterface
    {
      public:
        bool answerIncomingCall(sys::Service *service) override;
        bool hangupCall(sys::Service *service) override;
    };
    class HSP : public Profile
    {
        static constexpr auto CLASS_OF_DEVICE = 0x400204;


@@ 31,6 43,17 @@ namespace bluetooth
        void disconnect() override;
        void start() override;
        void stop() override;
        /// @brief Starts ring
        /// @return Success
        [[nodiscard]] auto startRinging() const noexcept -> Error::Code override;
        /// @brief Stops ring
        /// @return Success
        [[nodiscard]] auto stopRinging() const noexcept -> Error::Code override;
        /// @brief Initializes bluetooth audio call which is divided into two parts:
        /// - Ring stop
        /// - SCO link establishment
        /// @return Success
        [[nodiscard]] auto initializeCall() const noexcept -> Error::Code override;

        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override
        {}

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +6 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 19,6 19,9 @@ namespace bluetooth
        auto init() -> Error::Code;
        void start();
        void stop();
        void startRinging() const noexcept;
        void stopRinging() const noexcept;
        void initializeCall() const noexcept;
        void connect();
        void disconnect();
        void setDeviceAddress(bd_addr_t addr);


@@ 29,11 32,13 @@ namespace bluetooth
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static void processHCIEvent(uint8_t *event);
        static void processHSPEvent(uint8_t *event);
        static void establishAudioConnection();
        static std::array<uint8_t, serviceBufferLength> serviceBuffer;
        static constexpr uint8_t rfcommChannelNr = 1;
        static std::string agServiceName;
        static uint16_t scoHandle;
        static std::unique_ptr<SCO> sco;
        static std::unique_ptr<CellularInterface> cellularInterface;
        static std::array<char, commandBufferLength> ATcommandBuffer;
        static bd_addr_t deviceAddr;
        static const sys::Service *ownerService;

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +9 -0
@@ 24,6 24,15 @@ namespace bluetooth
        virtual void stop()                                                                       = 0;
        virtual void disconnect()                                                                 = 0;
        virtual void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) = 0;
        /// Starts ringing
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto startRinging() const noexcept -> Error::Code = 0;
        /// Stops ringing
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto stopRinging() const noexcept -> Error::Code = 0;
        /// Initializes call
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto initializeCall() const noexcept -> Error::Code = 0;

      protected:
        static void initSdp();

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +15 -0
@@ 95,6 95,21 @@ namespace bluetooth
        return Error::Success;
    }

    auto ProfileManager::startRinging() -> Error::Code
    {
        return currentProfilePtr->startRinging();
    }

    auto ProfileManager::stopRinging() -> Error::Code
    {
        return currentProfilePtr->stopRinging();
    }

    auto ProfileManager::initializeCall() -> Error::Code
    {
        return currentProfilePtr->initializeCall();
    }

    auto ProfileManager::setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code
    {
        if (currentProfilePtr == nullptr) {

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +3 -0
@@ 46,6 46,9 @@ namespace bluetooth

        auto start() -> Error::Code;
        auto stop() -> Error::Code;
        auto startRinging() -> Error::Code;
        auto stopRinging() -> Error::Code;
        auto initializeCall() -> Error::Code;

        auto setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code;


A module-bluetooth/bt_hsp_incoming_connection_establishment.puml => module-bluetooth/bt_hsp_incoming_connection_establishment.puml +15 -0
@@ 0,0 1,15 @@
@startuml

participant "Bluetooth Headset" as HS
participant "Pure Phone" as AG

    activate HS
    activate AG
    AG -> HS : Connection establishment
    AG -> HS : Ring
    ...
     -> HS : User initiated action
    HS -> AG : AT+CKPD=200
    AG -> HS : OK
    AG -> HS : SCO link establishment    
@enduml
\ No newline at end of file

A module-bluetooth/bt_hsp_incoming_connection_establishment.svg => module-bluetooth/bt_hsp_incoming_connection_establishment.svg +28 -0
@@ 0,0 1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="301px" preserveAspectRatio="none" style="width:412px;height:301px;" version="1.1" viewBox="0 0 412 301" width="412px" zoomAndPan="magnify"><defs><filter height="300%" id="fcrkdomc6dxrv" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="58.2656" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="150" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="150" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="160" x2="160" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="160" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="117.5313" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="150" y="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="150" y1="134.5625" y2="252.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="160" x2="160" y1="134.5625" y2="252.0938"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="58.2656" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="351" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="351" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="361" x2="361" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="361" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="117.5313" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="351" y="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="351" y1="134.5625" y2="252.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="361" x2="361" y1="134.5625" y2="252.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="155" x2="155" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="155" x2="155" y1="106.5625" y2="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="155" x2="155" y1="134.5625" y2="261.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="355.5" x2="355.5" y1="38.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 1.0,4.0;" x1="355.5" x2="355.5" y1="106.5625" y2="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="355.5" x2="355.5" y1="134.5625" y2="261.0938"/><rect fill="#FEFECE" filter="url(#fcrkdomc6dxrv)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="144" x="81" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="130" x="88" y="22.9951">Bluetooth Headset</text><rect fill="#FEFECE" filter="url(#fcrkdomc6dxrv)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="144" x="81" y="260.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="130" x="88" y="280.0889">Bluetooth Headset</text><rect fill="#FEFECE" filter="url(#fcrkdomc6dxrv)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="95" x="306.5" y="3"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="313.5" y="22.9951">Pure Phone</text><rect fill="#FEFECE" filter="url(#fcrkdomc6dxrv)" height="30.2969" style="stroke: #A80036; stroke-width: 1.5;" width="95" x="306.5" y="260.0938"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="81" x="313.5" y="280.0889">Pure Phone</text><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="58.2656" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="150" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="150" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="160" x2="160" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="160" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="117.5313" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="150" y="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="150" x2="150" y1="134.5625" y2="252.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="160" x2="160" y1="134.5625" y2="252.0938"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="58.2656" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="351" y="48.2969"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="351" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="361" x2="361" y1="48.2969" y2="106.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="361" y1="48.2969" y2="48.2969"/><rect fill="#FFFFFF" filter="url(#fcrkdomc6dxrv)" height="117.5313" style="stroke: #FFFFFF; stroke-width: 1.0;" width="10" x="351" y="134.5625"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="351" x2="351" y1="134.5625" y2="252.0938"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="361" x2="361" y1="134.5625" y2="252.0938"/><polygon fill="#A80036" points="171,65.4297,161,69.4297,171,73.4297,167,69.4297" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="165" x2="350" y1="69.4297" y2="69.4297"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="167" x="177" y="64.3638">Connection establishment</text><polygon fill="#A80036" points="171,94.5625,161,98.5625,171,102.5625,167,98.5625" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="165" x2="350" y1="98.5625" y2="98.5625"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="27" x="177" y="93.4966">Ring</text><polygon fill="#A80036" points="138,151.6953,148,155.6953,138,159.6953,142,155.6953" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="3" x2="144" y1="155.6953" y2="155.6953"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="128" x="10" y="150.6294">User initiated action</text><polygon fill="#A80036" points="339,180.8281,349,184.8281,339,188.8281,343,184.8281" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="160" x2="345" y1="184.8281" y2="184.8281"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="97" x="167" y="179.7622">AT+CKPD=200</text><polygon fill="#A80036" points="171,209.9609,161,213.9609,171,217.9609,167,213.9609" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="165" x2="350" y1="213.9609" y2="213.9609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="18" x="177" y="208.895">OK</text><polygon fill="#A80036" points="171,239.0938,161,243.0938,171,247.0938,167,243.0938" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.0;" x1="165" x2="350" y1="243.0938" y2="243.0938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="148" x="177" y="238.0278">SCO link establishment</text><!--
@startuml

participant "Bluetooth Headset" as HS
participant "Pure Phone" as AG

    activate HS
    activate AG
    AG -> HS : Connection establishment
    AG -> HS : Ring
    ...
     -> HS : User initiated action
    HS -> AG : AT+CKPD=200
    AG -> HS : OK
    AG -> HS : SCO link establishment    
@enduml

PlantUML version 1.2018.13(Mon Nov 26 18:11:51 CET 2018)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 11.0.10+9-Ubuntu-0ubuntu1.20.04
Operating System: Linux
OS Version: 5.8.0-50-generic
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

A module-bluetooth/bt_hsp_ring_handler.puml => module-bluetooth/bt_hsp_ring_handler.puml +15 -0
@@ 0,0 1,15 @@
@startuml
start
note right
    Ringing
end note
while (Call terminated) is (no)
    if (Answer a call) then (no)
    else (yes)
        :SCO link established;
        break;
    endif
endwhile
:Stop Ringing;
stop
@enduml

A module-bluetooth/bt_hsp_ring_handler.svg => module-bluetooth/bt_hsp_ring_handler.svg +28 -0
@@ 0,0 1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="360px" preserveAspectRatio="none" style="width:237px;height:360px;" version="1.1" viewBox="0 0 237 360" width="237px" zoomAndPan="magnify"><defs><filter height="300%" id="f1j9yvllgez01x" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><path d="M147.5,10 L147.5,18.5664 L127.5,22.5664 L147.5,26.5664 L147.5,35.1328 A0,0 0 0 0 147.5,35.1328 L214.5,35.1328 A0,0 0 0 0 214.5,35.1328 L214.5,20 L204.5,10 L147.5,10 A0,0 0 0 0 147.5,10 " fill="#FBFB77" filter="url(#f1j9yvllgez01x)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M204.5,10 L204.5,20 L214.5,20 L204.5,10 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="46" x="153.5" y="27.0669">Ringing</text><ellipse cx="117.5" cy="22.5664" fill="#000000" filter="url(#f1j9yvllgez01x)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#f1j9yvllgez01x)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="147" x="44" y="159.2456"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="127" x="54" y="180.3843">SCO link established</text><polygon fill="#FEFECE" filter="url(#f1j9yvllgez01x)" points="79.5,110.8433,155.5,110.8433,167.5,122.8433,155.5,134.8433,79.5,134.8433,67.5,122.8433,79.5,110.8433" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="121.5" y="145.0537">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="76" x="79.5" y="126.6514">Answer a call</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="14" x="167.5" y="120.249">no</text><polygon fill="#FEFECE" filter="url(#f1j9yvllgez01x)" points="74,55.1328,161,55.1328,173,67.1328,161,79.1328,74,79.1328,62,67.1328,74,55.1328" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="14" x="121.5" y="89.3433">no</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="87" x="74" y="70.9409">Call terminated</text><rect fill="#FEFECE" filter="url(#f1j9yvllgez01x)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="99" x="68" y="275.2144"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="79" x="78" y="296.353">Stop Ringing</text><ellipse cx="117.5" cy="339.1831" fill="none" filter="url(#f1j9yvllgez01x)" rx="10" ry="10" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="118" cy="339.6831" fill="#000000" filter="url(#f1j9yvllgez01x)" rx="6" ry="6" style="stroke: none; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="193.2144" y2="207.2144"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="24" y1="207.2144" y2="207.2144"/><polygon fill="#A80036" points="34,203.2144,24,207.2144,34,211.2144,30,207.2144" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="134.8433" y2="159.2456"/><polygon fill="#A80036" points="113.5,149.2456,117.5,159.2456,121.5,149.2456,117.5,153.2456" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="167.5" x2="225" y1="122.8433" y2="122.8433"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="225" x2="225" y1="67.1328" y2="122.8433"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="225" x2="173" y1="67.1328" y2="67.1328"/><polygon fill="#A80036" points="183,63.1328,173,67.1328,183,71.1328,179,67.1328" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="79.1328" y2="110.8433"/><polygon fill="#A80036" points="113.5,100.8433,117.5,110.8433,121.5,100.8433,117.5,104.8433" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="62" x2="24" y1="67.1328" y2="67.1328"/><polygon fill="#A80036" points="20,162.3276,24,172.3276,28,162.3276,24,166.3276" style="stroke: #A80036; stroke-width: 1.5;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="24" x2="24" y1="67.1328" y2="255.2144"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="24" x2="117.5" y1="255.2144" y2="255.2144"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="255.2144" y2="275.2144"/><polygon fill="#A80036" points="113.5,265.2144,117.5,275.2144,121.5,265.2144,117.5,269.2144" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="32.5664" y2="55.1328"/><polygon fill="#A80036" points="113.5,45.1328,117.5,55.1328,121.5,45.1328,117.5,49.1328" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="117.5" x2="117.5" y1="309.1831" y2="329.1831"/><polygon fill="#A80036" points="113.5,319.1831,117.5,329.1831,121.5,319.1831,117.5,323.1831" style="stroke: #A80036; stroke-width: 1.0;"/><!--
@startuml
start
note right
    Ringing
end note
while (Call terminated) is (no)
    if (Answer a call) then (no)
    else (yes)
        :SCO link established;
        break;
    endif
endwhile
:Stop Ringing;
stop
@enduml

PlantUML version 1.2018.13(Mon Nov 26 18:11:51 CET 2018)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 11.0.10+9-Ubuntu-0ubuntu1.20.04
Operating System: Linux
OS Version: 5.8.0-50-generic
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

A module-bluetooth/bt_hsp_ring_trigger.puml => module-bluetooth/bt_hsp_ring_trigger.puml +14 -0
@@ 0,0 1,14 @@
@startuml
start
note right
    Call incoming
end note
:Service Cellular;
:Application call;
:Service Audio;
:Service Bluetooth;
if (Current profile allows ringing) then (yes)
    :Ring;
endif
stop
@enduml

A module-bluetooth/bt_hsp_ring_trigger.svg => module-bluetooth/bt_hsp_ring_trigger.svg +27 -0
@@ 0,0 1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="448px" preserveAspectRatio="none" style="width:263px;height:448px;" version="1.1" viewBox="0 0 263 448" width="263px" zoomAndPan="magnify"><defs><filter height="300%" id="fxo3rzh2br75o" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><path d="M145,10 L145,18.5664 L125,22.5664 L145,26.5664 L145,35.1328 A0,0 0 0 0 145,35.1328 L251,35.1328 A0,0 0 0 0 251,35.1328 L251,20 L241,10 L145,10 A0,0 0 0 0 145,10 " fill="#FBFB77" filter="url(#fxo3rzh2br75o)" style="stroke: #A80036; stroke-width: 1.0;"/><path d="M241,10 L241,20 L251,20 L241,10 " fill="#FBFB77" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="85" x="151" y="27.0669">Call incoming</text><ellipse cx="115" cy="22.5664" fill="#000000" filter="url(#fxo3rzh2br75o)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#fxo3rzh2br75o)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="115" x="57.5" y="55.1328"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="95" x="67.5" y="76.2715">Service Cellular</text><rect fill="#FEFECE" filter="url(#fxo3rzh2br75o)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="114" x="58" y="109.1016"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="94" x="68" y="130.2402">Application call</text><rect fill="#FEFECE" filter="url(#fxo3rzh2br75o)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="104" x="63" y="163.0703"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="84" x="73" y="184.209">Service Audio</text><rect fill="#FEFECE" filter="url(#fxo3rzh2br75o)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="130" x="50" y="217.0391"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="110" x="60" y="238.1777">Service Bluetooth</text><rect fill="#FEFECE" filter="url(#fxo3rzh2br75o)" height="33.9688" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="47" x="91.5" y="319.4102"/><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacingAndGlyphs" textLength="27" x="101.5" y="340.5488">Ring</text><polygon fill="#FEFECE" filter="url(#fxo3rzh2br75o)" points="32,271.0078,198,271.0078,210,283.0078,198,295.0078,32,295.0078,20,283.0078,32,271.0078" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="20" x="119" y="305.2183">yes</text><text fill="#000000" font-family="sans-serif" font-size="11" lengthAdjust="spacingAndGlyphs" textLength="166" x="32" y="286.8159">Current profile allows ringing</text><polygon fill="#FEFECE" filter="url(#fxo3rzh2br75o)" points="115,373.3789,127,385.3789,115,397.3789,103,385.3789,115,373.3789" style="stroke: #A80036; stroke-width: 1.5;"/><ellipse cx="115" cy="427.3789" fill="none" filter="url(#fxo3rzh2br75o)" rx="10" ry="10" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="115.5" cy="427.8789" fill="#000000" filter="url(#fxo3rzh2br75o)" rx="6" ry="6" style="stroke: none; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="32.5664" y2="55.1328"/><polygon fill="#A80036" points="111,45.1328,115,55.1328,119,45.1328,115,49.1328" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="89.1016" y2="109.1016"/><polygon fill="#A80036" points="111,99.1016,115,109.1016,119,99.1016,115,103.1016" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="143.0703" y2="163.0703"/><polygon fill="#A80036" points="111,153.0703,115,163.0703,119,153.0703,115,157.0703" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="197.0391" y2="217.0391"/><polygon fill="#A80036" points="111,207.0391,115,217.0391,119,207.0391,115,211.0391" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="295.0078" y2="319.4102"/><polygon fill="#A80036" points="111,309.4102,115,319.4102,119,309.4102,115,313.4102" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="210" x2="222" y1="283.0078" y2="283.0078"/><polygon fill="#A80036" points="218,326.3945,222,336.3945,226,326.3945,222,330.3945" style="stroke: #A80036; stroke-width: 1.5;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="222" x2="222" y1="283.0078" y2="385.3789"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="222" x2="127" y1="385.3789" y2="385.3789"/><polygon fill="#A80036" points="137,381.3789,127,385.3789,137,389.3789,133,385.3789" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="353.3789" y2="373.3789"/><polygon fill="#A80036" points="111,363.3789,115,373.3789,119,363.3789,115,367.3789" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="251.0078" y2="271.0078"/><polygon fill="#A80036" points="111,261.0078,115,271.0078,119,261.0078,115,265.0078" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="115" x2="115" y1="397.3789" y2="417.3789"/><polygon fill="#A80036" points="111,407.3789,115,417.3789,119,407.3789,115,411.3789" style="stroke: #A80036; stroke-width: 1.0;"/><!--
@startuml
start
note right
    Call incoming
end note
:Service Cellular;
:Application call;
:Service Audio;
:Service Bluetooth;
if (Current profile allows ringing) then (yes)
    :Ring;
endif
stop
@enduml

PlantUML version 1.2018.13(Mon Nov 26 18:11:51 CET 2018)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 11.0.10+9-Ubuntu-0ubuntu1.20.04
Operating System: Linux
OS Version: 5.8.0-50-generic
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +16 -3
@@ 10,6 10,8 @@
#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/ServiceBluetoothCommon.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>
#include <service-bluetooth/messages/AudioRouting.hpp>
#include <service-bluetooth/messages/Ring.hpp>
#include <service-db/Settings.hpp>
#include <service-evtmgr/EventManagerServiceAPI.hpp>



@@ 319,9 321,16 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
        auto input = audioMux.GetPlaybackInput(playbackType);
        // stop bluetooth stream if available
        if (bluetoothConnected) {
            LOG_DEBUG("Sending Bluetooth start stream request");
            bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Play),
                            service::name::bluetooth);
            if (playbackType == audio::PlaybackType::CallRingtone) {
                LOG_DEBUG("Sending Bluetooth start ringing");
                bus.sendUnicast(std::make_shared<message::bluetooth::Ring>(message::bluetooth::Ring::State::Enable),
                                service::name::bluetooth);
            }
            else {
                LOG_DEBUG("Sending Bluetooth start stream request");
                bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Play),
                                service::name::bluetooth);
            }
        }

        AudioStart(input);


@@ 334,6 343,10 @@ std::unique_ptr<AudioResponseMessage> ServiceAudio::HandleStart(const Operation:
    }
    else if (opType == Operation::Type::Router) {
        auto input = audioMux.GetRoutingInput(true);
        if (bluetoothConnected) {
            LOG_DEBUG("Sending Bluetooth start routing");
            bus.sendUnicast(std::make_shared<message::bluetooth::StartAudioRouting>(), service::name::bluetooth);
        }
        AudioStart(input);
        return std::make_unique<AudioStartRoutingResponse>(retCode, retToken);
    }

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +18 -0
@@ 11,6 11,7 @@
#include <Service/Message.hpp>
#include <service-db/Settings.hpp>
#include "service-bluetooth/messages/AudioVolume.hpp"
#include "service-bluetooth/messages/AudioRouting.hpp"
#include "service-bluetooth/messages/Connect.hpp"
#include "service-bluetooth/messages/Disconnect.hpp"
#include "service-bluetooth/messages/Status.hpp"


@@ 18,6 19,7 @@
#include "service-bluetooth/messages/BondedDevices.hpp"
#include "service-bluetooth/messages/Unpair.hpp"
#include "service-bluetooth/messages/SetDeviceName.hpp"
#include "service-bluetooth/messages/Ring.hpp"

#include "SystemManager/messages/SentinelRegistrationMessage.hpp"



@@ 76,6 78,8 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    connectHandler<BluetoothMessage>();
    connectHandler<BluetoothPairMessage>();
    connectHandler<message::bluetooth::AudioVolume>();
    connectHandler<message::bluetooth::Ring>();
    connectHandler<message::bluetooth::StartAudioRouting>();
    connectHandler<message::bluetooth::Connect>();
    connectHandler<message::bluetooth::ConnectResult>();
    connectHandler<message::bluetooth::Disconnect>();


@@ 319,6 323,20 @@ auto ServiceBluetooth::handle(message::bluetooth::AudioVolume *msg) -> std::shar
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::Ring *msg) -> std::shared_ptr<sys::Message>
{
    const auto enableRing = msg->enabled();
    sendWorkerCommand(bluetooth::Command(enableRing ? bluetooth::Command::Type::StartRinging
                                                    : bluetooth::Command::Type::StopRinging));
    return std::make_shared<sys::ResponseMessage>();
}

auto ServiceBluetooth::handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartRouting));
    return std::make_shared<sys::ResponseMessage>();
}

void ServiceBluetooth::startTimeoutTimer()
{
    if (connectionTimeoutTimer.isValid()) {

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +4 -1
@@ 45,7 45,8 @@ namespace message::bluetooth
    class Disconnect;
    class DisconnectResult;
    class AudioVolume;

    class Ring;
    class StartAudioRouting;
} // namespace message::bluetooth

class ServiceBluetooth : public sys::Service


@@ 93,6 94,8 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothAudioStartMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::AudioVolume *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::Ring *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr<sys::Message>;
};

namespace sys

A module-services/service-bluetooth/service-bluetooth/messages/AudioRouting.hpp => module-services/service-bluetooth/service-bluetooth/messages/AudioRouting.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "service-bluetooth/BluetoothMessage.hpp"

namespace message::bluetooth
{
    class StartAudioRouting : public BluetoothMessage
    {};
} // namespace message::bluetooth

A module-services/service-bluetooth/service-bluetooth/messages/Ring.hpp => module-services/service-bluetooth/service-bluetooth/messages/Ring.hpp +32 -0
@@ 0,0 1,32 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "service-bluetooth/BluetoothMessage.hpp"

namespace message::bluetooth
{
    /// @brief Message that indicates whether to enable or disable bluetooth ring
    class Ring : public BluetoothMessage
    {
      public:
        enum class State : bool
        {
            Enable,
            Disable
        };

        explicit Ring(State state) : state{state}
        {}

        /// @return True if bluetooth should ring, false otherwise
        [[nodiscard]] auto enabled() const noexcept -> bool
        {
            return state == State::Enable;
        }

      private:
        const State state;
    };
} // namespace message::bluetooth

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +9 -6
@@ 500,7 500,8 @@ void ServiceCellular::registerMessageHandlers()

    connect(typeid(CellularHangupCallMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularHangupCallMessage *>(request);
        return handleCellularHangupCallMessage(msg);
        handleCellularHangupCallMessage(msg);
        return sys::MessageNone{};
    });

    connect(typeid(db::QueryResponse), [&](sys::Message *request) -> sys::MessagePointer {


@@ 2291,8 2292,7 @@ auto ServiceCellular::handleCellularCallRequestMessage(CellularCallRequestMessag
    return std::make_shared<CellularResponseMessage>(request->isHandled());
}

auto ServiceCellular::handleCellularHangupCallMessage(CellularHangupCallMessage *msg)
    -> std::shared_ptr<CellularResponseMessage>
void ServiceCellular::handleCellularHangupCallMessage(CellularHangupCallMessage *msg)
{
    auto channel = cmux->get(TS0710::Channel::Commands);
    LOG_INFO("CellularHangupCall");


@@ 2303,14 2303,17 @@ auto ServiceCellular::handleCellularHangupCallMessage(CellularHangupCallMessage 
            if (!ongoingCall.endCall(CellularCall::Forced::True)) {
                LOG_ERROR("Failed to end ongoing call");
            }
            return std::make_shared<CellularResponseMessage>(true, msg->messageType);
            bus.sendMulticast(std::make_shared<CellularResponseMessage>(true, msg->messageType),
                              sys::BusChannel::ServiceCellularNotifications);
        }
        else {
            LOG_ERROR("Call not aborted");
            return std::make_shared<CellularResponseMessage>(false, msg->messageType);
            bus.sendMulticast(std::make_shared<CellularResponseMessage>(false, msg->messageType),
                              sys::BusChannel::ServiceCellularNotifications);
        }
    }
    return std::make_shared<CellularResponseMessage>(false, msg->messageType);
    bus.sendMulticast(std::make_shared<CellularResponseMessage>(false, msg->messageType),
                      sys::BusChannel::ServiceCellularNotifications);
}

auto ServiceCellular::handleDBQueryResponseMessage(db::QueryResponse *msg) -> std::shared_ptr<sys::ResponseMessage>

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +1 -1
@@ 341,7 341,7 @@ class ServiceCellular : public sys::Service

    auto handleCellularAnswerIncomingCallMessage(CellularMessage *msg) -> std::shared_ptr<CellularResponseMessage>;
    auto handleCellularCallRequestMessage(CellularCallRequestMessage *msg) -> std::shared_ptr<CellularResponseMessage>;
    auto handleCellularHangupCallMessage(CellularHangupCallMessage *msg) -> std::shared_ptr<CellularResponseMessage>;
    void handleCellularHangupCallMessage(CellularHangupCallMessage *msg);
    auto handleDBQueryResponseMessage(db::QueryResponse *msg) -> std::shared_ptr<sys::ResponseMessage>;
    auto handleCellularListCallsMessage(CellularMessage *msg) -> std::shared_ptr<sys::ResponseMessage>;
    auto handleDBNotificatioMessage(db::NotificationMessage *msg) -> std::shared_ptr<sys::ResponseMessage>;

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +1 -0
@@ 15,6 15,7 @@ namespace Log
                                                            {"ServiceCellular", logger_level::LOGINFO},
                                                            {"ServiceAntenna", logger_level::LOGINFO},
                                                            {"ServiceAudio", logger_level::LOGINFO},
                                                            {"ServiceBluetooth", logger_level::LOGINFO},
                                                            {"ServiceFota", logger_level::LOGINFO},
                                                            {"ServiceEink", logger_level::LOGINFO},
                                                            {"ServiceDB", logger_level::LOGINFO},