~aleteoryx/muditaos

6ed82f3fa3a0aa63a50281ccb7ea90019da3d3be — Lefucjusz 2 years ago 8021ebb
[MOS-1005] Fix handsfree ringing after caller hangs up

* Fix of the issue that incoming call triggered
handsfree ringing which didn't stop after
calling side hung up.
* Cleanup of several places in code, minor
bluetooth <-> cellular integration refactor.
41 files changed, 1486 insertions(+), 1326 deletions(-)

M module-apps/application-settings/ApplicationSettings.cpp
M module-bluetooth/Bluetooth/AbstractController.hpp
M module-bluetooth/Bluetooth/BluetoothStateMachine.hpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BtCommand.hpp
M module-bluetooth/Bluetooth/CommandHandler.cpp
M module-bluetooth/Bluetooth/CommandHandler.hpp
R module-bluetooth/Bluetooth/{Error => Result}.hpp
M module-bluetooth/Bluetooth/WorkerController.cpp
M module-bluetooth/Bluetooth/WorkerController.hpp
M module-bluetooth/Bluetooth/command/event/Events.hpp
M module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp
M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp
M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp
M module-bluetooth/Bluetooth/interface/profiles/BtCommand.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp
M module-bluetooth/tests/tests-StatefulController.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
D module-services/service-bluetooth/service-bluetooth/messages/AudioRouting.hpp
M module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp
M module-services/service-cellular/QMBNManager.hpp
M pure_changelog.md
M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <application-settings/ApplicationSettings.hpp>


@@ 251,7 251,8 @@ namespace app
            auto connectResultMsg = static_cast<::message::bluetooth::ConnectResult *>(msg);
            auto device           = connectResultMsg->getDevice();

            if (connectResultMsg->isSucceed()) {
            using message::bluetooth::ConnectResult;
            if (connectResultMsg->getResult() == ConnectResult::Result::Success) {
                return sys::MessageNone{};
            }


M module-bluetooth/Bluetooth/AbstractController.hpp => module-bluetooth/Bluetooth/AbstractController.hpp +7 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 18,14 18,13 @@ namespace bluetooth::event
    struct ShutDown;
    struct Pair;
    struct Unpair;
    struct StartRinging;
    struct StopRinging;
    struct StartRouting;
    struct StartStream;
    struct StopStream;
    struct CallAnswered;
    struct CallTerminated;
    struct CallStarted;
    struct CallMissed;
    struct OutgoingCallStarted;
    struct IncomingCallStarted;
    struct IncomingCallNumber;
    struct SignalStrengthData;
    struct OperatorNameData;


@@ 35,7 34,6 @@ namespace bluetooth::event

namespace bluetooth
{

    class AbstractController
    {
      public:


@@ 54,14 52,13 @@ namespace bluetooth
        virtual void handle(const bluetooth::event::ShutDown &evt)            = 0;
        virtual void handle(const bluetooth::event::Pair &evt)                = 0;
        virtual void handle(const bluetooth::event::Unpair &evt)              = 0;
        virtual void handle(const bluetooth::event::StartRinging &evt)        = 0;
        virtual void handle(const bluetooth::event::StopRinging &evt)         = 0;
        virtual void handle(const bluetooth::event::StartRouting &evt)        = 0;
        virtual void handle(const bluetooth::event::StartStream &evt)         = 0;
        virtual void handle(const bluetooth::event::StopStream &evt)          = 0;
        virtual void handle(const bluetooth::event::CallAnswered &evt)        = 0;
        virtual void handle(const bluetooth::event::CallTerminated &evt)      = 0;
        virtual void handle(const bluetooth::event::CallStarted &evt)         = 0;
        virtual void handle(const bluetooth::event::CallMissed &evt)          = 0;
        virtual void handle(const bluetooth::event::OutgoingCallStarted &evt) = 0;
        virtual void handle(const bluetooth::event::IncomingCallStarted &evt) = 0;
        virtual void handle(const bluetooth::event::IncomingCallNumber &evt)  = 0;
        virtual void handle(const bluetooth::event::SignalStrengthData &evt)  = 0;
        virtual void handle(const bluetooth::event::OperatorNameData &evt)    = 0;

M module-bluetooth/Bluetooth/BluetoothStateMachine.hpp => module-bluetooth/Bluetooth/BluetoothStateMachine.hpp +53 -53
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 43,7 43,7 @@ namespace bluetooth
                throw std::runtime_error("shouldn't happen");
            }
            // printf("driver: 0x%X %d\n", driver.get(), int(driver.use_count()));
            if (const auto status = driver->init(); status != Error::Success) {
            if (const auto status = driver->init(); status != Result::Code::Success) {
                throw InitializationError{"Unable to initialize a bluetooth driver."};
            }
        }


@@ 75,7 75,7 @@ namespace bluetooth
    {
        void operator()(DeviceRegistrationFunction registerDevice, InitializationState &data)
        {
            if (const auto status = registerDevice(); status != Error::Success) {
            if (const auto status = registerDevice(); status != Result::Code::Success) {
                throw InitializationError{"Unable to initialize bluetooth"};
            }
            data.isInitDone = true;


@@ 86,7 86,7 @@ namespace bluetooth
    {
        void operator()(std::shared_ptr<AbstractDriver> driver)
        {
            if (const auto status = driver->run(); status != Error::Success) {
            if (const auto status = driver->run(); status != Result::Code::Success) {
                throw InitializationError{"Unable to run the bluetooth driver"};
            }
        }


@@ 187,63 187,63 @@ namespace bluetooth

    } constexpr HandleUnsetVisibility;

    struct StartRinging
    struct IncomingCallStarted
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->startRinging();
            profileManager->incomingCallStarted();
        }
    } constexpr StartRinging;
    } constexpr IncomingCallStarted;

    struct StopRinging
    struct IncomingCallNumber
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                        bluetooth::event::IncomingCallNumber evt)
        {
            profileManager->stopRinging();
            profileManager->setIncomingCallNumber(evt.number);
        }
    } constexpr StopRinging;
    } constexpr IncomingCallNumber;

    struct InitializeCall
    struct OutgoingCallStarted
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                        bluetooth::event::OutgoingCallStarted evt)
        {
            profileManager->initializeCall();
            profileManager->outgoingCallStarted(evt.number);
        }
    } constexpr InitializeCall;
    } constexpr OutgoingCallStarted;

    struct CallAnswered
    struct IncomingCallAnswered
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->callAnswered();
            profileManager->incomingCallAnswered();
        }
    } constexpr CallAnswered;
    } constexpr IncomingCallAnswered;

    struct TerminateCall
    struct OutgoingCallAnswered
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->terminateCall();
            profileManager->outgoingCallAnswered();
        }
    } constexpr TerminateCall;
    } constexpr OutgoingCallAnswered;

    struct CallStarted
    struct CallTerminated
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                        bluetooth::event::CallStarted evt)
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->callStarted(evt.number);
            profileManager->callTerminated();
        }
    } constexpr CallStarted;
    } constexpr CallTerminated;

    struct IncomingCall
    struct CallMissed
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                        bluetooth::event::IncomingCallNumber evt)
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->setIncomingCallNumber(evt.number);
            profileManager->callMissed();
        }
    } constexpr IncomingCall;
    } constexpr CallMissed;

    struct SignalStrength
    {


@@ 314,26 314,24 @@ namespace bluetooth
        {
            using namespace sml;
            // clang-format off
            return make_transition_table(
                *"CallSetup"_s + sml::event<bluetooth::event::StartRinging> / StartRinging = "CallRinging"_s,
                "CallSetup"_s + sml::event<bluetooth::event::StartRouting> / InitializeCall = "CallInitiated"_s,
                "CallSetup"_s + sml::event<bluetooth::event::CallStarted> / CallStarted = "CallInitiated"_s,
                "CallSetup"_s + sml::event<bluetooth::event::IncomingCallNumber> / (StartRinging, IncomingCall)  = "CallRinging"_s,

                "CallRinging"_s + sml::event<bluetooth::event::StopRinging> / StopRinging = "CallDropped"_s,
                "CallRinging"_s + sml::event<bluetooth::event::CallAnswered> / CallAnswered = "CallInProgress"_s,
                "CallRinging"_s + sml::event<bluetooth::event::CallTerminated> / TerminateCall = "CallDropped"_s,
                return make_transition_table(
                        *"CallSetup"_s + sml::event<bluetooth::event::IncomingCallStarted> / IncomingCallStarted = "CallRinging"_s,
                        "CallSetup"_s + sml::event<bluetooth::event::IncomingCallNumber> / (IncomingCallStarted, IncomingCallNumber) = "CallRinging"_s,
                        "CallSetup"_s + sml::event<bluetooth::event::OutgoingCallStarted> / OutgoingCallStarted = "CallInitiated"_s,

                "CallInitiated"_s + sml::event<bluetooth::event::CallAnswered> / CallAnswered = "CallInProgress"_s,
               "CallInitiated"_s + sml::event<bluetooth::event::StopRinging> / StopRinging = "CallDropped"_s,
               "CallInitiated"_s + sml::event<bluetooth::event::CallTerminated> / TerminateCall = "CallDropped"_s,
                "CallInitiated"_s + sml::event<bluetooth::event::IncomingCallNumber> / IncomingCall= "CallInitiated"_s,
                        "CallRinging"_s + sml::event<bluetooth::event::IncomingCallNumber> / IncomingCallNumber = "CallRinging"_s,
                        "CallRinging"_s + sml::event<bluetooth::event::CallAnswered> / IncomingCallAnswered = "CallInProgress"_s,
                        "CallRinging"_s + sml::event<bluetooth::event::CallTerminated> / CallTerminated = "CallEnded"_s,
                        "CallRinging"_s + sml::event<bluetooth::event::CallMissed> / CallMissed = "CallEnded"_s,

                "CallInProgress"_s + sml::event<bluetooth::event::CallTerminated> / TerminateCall = "CallDropped"_s,
                        "CallInitiated"_s + sml::event<bluetooth::event::CallAnswered> / OutgoingCallAnswered = "CallInProgress"_s,
                        "CallInitiated"_s + sml::event<bluetooth::event::CallTerminated> / CallTerminated = "CallEnded"_s,

                "CallDropped"_s = X
                        "CallInProgress"_s + sml::event<bluetooth::event::CallTerminated> / CallTerminated = "CallEnded"_s,

            );
                        "CallEnded"_s = X
                );
            // clang-format on
        }
    };



@@ 342,7 340,10 @@ namespace bluetooth
    {
        auto operator()() const
        {
            auto forwardEvent = [](const auto &ev, auto &sm, auto &deps, auto &subs){sm.process_event(ev,deps,subs);};
            const auto forwardEvent = [](const auto &ev, auto &sm, auto &deps, auto &subs) {
                sm.process_event(ev, deps, subs);
            };

            using namespace sml;
            // clang-format off
                return make_transition_table(


@@ 364,13 365,12 @@ namespace bluetooth
                        state<Idle> + sml::event<bluetooth::event::StartStream>/ StartAudio = state<Idle>,
                        state<Idle> + sml::event<bluetooth::event::StopStream>/ StopAudio = state<Idle>,

                        state<Idle> + sml::event<bluetooth::event::StartRouting> / forwardEvent= state<Call>,
                        state<Idle> + sml::event<bluetooth::event::IncomingCallNumber>  / forwardEvent  = state<Call>,
                        state<Idle> + sml::event<bluetooth::event::CallStarted> / forwardEvent= state<Call>,
                        state<Idle> + sml::event<bluetooth::event::IncomingCallStarted> / forwardEvent = state<Call>,
                        state<Idle> + sml::event<bluetooth::event::IncomingCallNumber> / forwardEvent = state<Call>,
                        state<Idle> + sml::event<bluetooth::event::OutgoingCallStarted> / forwardEvent = state<Call>,

                        state<Call> = state<Idle> // this one is needed to go out from Call substate properly!

                            );
                );
            // clang-format on
        }
    };

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-bluetooth/ServiceBluetooth.hpp>


@@ 36,7 36,7 @@ namespace queues

namespace
{
    constexpr auto BluetoothWorkerStackDepth = 4096U;
    constexpr auto BluetoothWorkerStackDepth = 1024 * 4;

    class DeviceRegistration
    {


@@ 61,7 61,7 @@ namespace
            bluetooth::set_name(settingsName);

            settings->onLinkKeyAdded = onLinkKeyAdded;
            return bluetooth::Error::Success;
            return bluetooth::Result::Code::Success;
        }

      private:

M module-bluetooth/Bluetooth/BtCommand.hpp => module-bluetooth/Bluetooth/BtCommand.hpp +3 -4
@@ 1,13 1,12 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Error.hpp"

#include "Result.hpp"
#include <string>

namespace bluetooth
{
    auto set_name(std::string &name) -> Error;
    auto set_name(std::string &name) -> Result;
} // namespace bluetooth

M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +23 -23
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CommandHandler.hpp"


@@ 23,7 23,7 @@ namespace bluetooth
{
    namespace
    {
        [[nodiscard]] auto toString(bluetooth::Error::Code code) -> std::string
        [[nodiscard]] auto toString(bluetooth::Result::Code code) -> std::string
        {
            return utils::enumToString(code);
        }


@@ 39,72 39,72 @@ namespace bluetooth
        this->driver->registerPowerOnCallback([profilePtr = this->profileManager]() { profilePtr->init(); });
    }

    Error::Code CommandHandler::scan()
    Result::Code CommandHandler::scan()
    {

        if (const auto ret = driver->scan(); ret.err != bluetooth::Error::Success) {
            LOG_ERROR("Cant start scan!: %s %" PRIu32 "", toString(ret.err).c_str(), ret.lib_code);
            return ret.err;
        if (const auto ret = driver->scan(); ret.result != bluetooth::Result::Code::Success) {
            LOG_ERROR("Can't start scan!: %s %d", toString(ret.result).c_str(), ret.libraryResult);
            return ret.result;
        }

        LOG_INFO("Scan started!");
        // open new scan window
        return Error::Success;
        return Result::Code::Success;
    }

    Error::Code CommandHandler::stopScan()
    Result::Code CommandHandler::stopScan()
    {
        LOG_INFO("Stopping scan!");
        driver->stopScan();
        return Error::Success;
        return Result::Code::Success;
    }

    Error::Code CommandHandler::setVisibility(bool visibility)
    Result::Code CommandHandler::setVisibility(bool visibility)
    {
        driver->setVisibility(visibility);
        settings->setValue(bluetooth::Settings::Visibility, static_cast<int>(visibility));
        return Error::Success;
        return Result::Code::Success;
    }

    Error::Code CommandHandler::connect(const DataVariant &data)
    Result::Code CommandHandler::connect(const DataVariant &data)
    {
        auto device = std::get<Devicei>(data);
        LOG_INFO("Connecting device");
        profileManager->connect(device);
        return Error::Success;
        return Result::Code::Success;
    }

    Error::Code CommandHandler::disconnect()
    Result::Code CommandHandler::disconnect()
    {
        LOG_INFO("Disconnecting device");
        profileManager->disconnect();
        return Error::Success;
        return Result::Code::Success;
    }
    Error::Code CommandHandler::pair(const DataVariant &data)

    Result::Code CommandHandler::pair(const DataVariant &data)
    {
        auto device = std::get<Devicei>(data);
        LOG_INFO("Pairing...");
        auto errorCode = Error::Code::Success;
        auto errorCode = Result::Code::Success;
        driver->pair(device);
        LOG_INFO("Pairing result: %s", magic_enum::enum_name(errorCode).data());
        return errorCode;
    }
    Error::Code CommandHandler::unpair(const DataVariant &data)

    Result::Code CommandHandler::unpair(const DataVariant &data)
    {
        auto device = std::get<Devicei>(data);
        LOG_INFO("Unpairing...");
        const auto errorCode = Error::Code::Success;
        const auto errorCode = Result::Code::Success;
        driver->unpair(device);
        LOG_INFO("Unpairing result: %s", magic_enum::enum_name(errorCode).data());
        return errorCode;
    }

    Error::Code CommandHandler::availableDevices()
    Result::Code CommandHandler::availableDevices()
    {
        auto msg = std::make_shared<message::bluetooth::ResponseVisibleDevices>(bluetooth::GAP::getDevicesList());
        static_cast<ServiceBluetooth *>(service)->bus.sendUnicast(std::move(msg), service::name::service_desktop);

        return Error::Success;
        return Result::Code::Success;
    }

} // namespace bluetooth

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +18 -18
@@ 1,9 1,9 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Error.hpp"
#include "Result.hpp"
#include "interface/profiles/ProfileManager.hpp"
#include "interface/BluetoothDriver.hpp"



@@ 27,14 27,14 @@ namespace bluetooth
      public:
        virtual ~AbstractCommandHandler() noexcept = default;

        virtual Error::Code scan()                           = 0;
        virtual Error::Code stopScan()                       = 0;
        virtual Error::Code setVisibility(bool visibility)   = 0;
        virtual Error::Code connect(const DataVariant &data) = 0;
        virtual Error::Code disconnect()                     = 0;
        virtual Error::Code pair(const DataVariant &data)    = 0;
        virtual Error::Code unpair(const DataVariant &data)  = 0;
        virtual Error::Code availableDevices()               = 0;
        virtual Result::Code scan()                           = 0;
        virtual Result::Code stopScan()                       = 0;
        virtual Result::Code setVisibility(bool visibility)   = 0;
        virtual Result::Code connect(const DataVariant &data) = 0;
        virtual Result::Code disconnect()                     = 0;
        virtual Result::Code pair(const DataVariant &data)    = 0;
        virtual Result::Code unpair(const DataVariant &data)  = 0;
        virtual Result::Code availableDevices()               = 0;
    };

    class CommandHandler : public AbstractCommandHandler


@@ 45,14 45,14 @@ namespace bluetooth
                                std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                                std::shared_ptr<bluetooth::AbstractDriver> driver);

        Error::Code scan() override;
        Error::Code stopScan() override;
        Error::Code setVisibility(bool visibility) override;
        Error::Code connect(const DataVariant &data) override;
        Error::Code disconnect() override;
        Error::Code pair(const DataVariant &data) override;
        Error::Code unpair(const DataVariant &data) override;
        Error::Code availableDevices() override;
        Result::Code scan() override;
        Result::Code stopScan() override;
        Result::Code setVisibility(bool visibility) override;
        Result::Code connect(const DataVariant &data) override;
        Result::Code disconnect() override;
        Result::Code pair(const DataVariant &data) override;
        Result::Code unpair(const DataVariant &data) override;
        Result::Code availableDevices() override;

      private:
        sys::Service *service;

R module-bluetooth/Bluetooth/Error.hpp => module-bluetooth/Bluetooth/Result.hpp +10 -11
@@ 1,28 1,27 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>
#include <variant>
#include <optional>

namespace bluetooth
{

    struct Error
    struct Result
    {
        enum Code
        enum class Code
        {
            Success,
            NotReady,
            SystemError,
            LibraryError,
        } err             = Success;
        uint32_t lib_code = 0;
        Error(enum Code err = Success, int lib_code = Success) : err(err)
        };

        Code result;
        int libraryResult;

        explicit Result(Code result = Code::Success, int libraryResult = 0)
            : result(result), libraryResult(libraryResult)
        {}
    };

} // namespace bluetooth
const char *c_str(bluetooth::Error::Code code);

M module-bluetooth/Bluetooth/WorkerController.cpp => module-bluetooth/Bluetooth/WorkerController.cpp +31 -12
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BluetoothStateMachine.hpp"


@@ 42,98 42,117 @@ namespace bluetooth
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::StopScan &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::GetDevicesAvailable &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::VisibilityOn &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::VisibilityOff &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::Connect &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::Disconnect &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::PowerOn &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::PowerOff &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::ShutDown &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::Pair &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::Unpair &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::StartRinging &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::StopRinging &evt)

    void StatefulController::handle(const bluetooth::event::StartStream &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::StartRouting &evt)

    void StatefulController::handle(const bluetooth::event::StopStream &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::StartStream &evt)

    void StatefulController::handle(const bluetooth::event::CallAnswered &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::StopStream &evt)

    void StatefulController::handle(const bluetooth::event::CallTerminated &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::CallAnswered &evt)

    void StatefulController::handle(const bluetooth::event::CallMissed &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::CallTerminated &evt)

    void StatefulController::handle(const bluetooth::event::OutgoingCallStarted &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bluetooth::event::CallStarted &evt)

    void StatefulController::handle(const bluetooth::event::IncomingCallStarted &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::IncomingCallNumber &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::SignalStrengthData &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::OperatorNameData &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::BatteryLevelData &evt)
    {
        pimpl->sm.process_event(evt);
    };

    void StatefulController::handle(const bluetooth::event::NetworkStatusData &evt)
    {
        pimpl->sm.process_event(evt);

M module-bluetooth/Bluetooth/WorkerController.hpp => module-bluetooth/Bluetooth/WorkerController.hpp +5 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 13,7 13,7 @@

namespace bluetooth
{
    using DeviceRegistrationFunction = std::function<Error::Code()>;
    using DeviceRegistrationFunction = std::function<Result::Code()>;

    class StatefulController : public AbstractController
    {


@@ 45,14 45,13 @@ namespace bluetooth
        void handle(const bluetooth::event::ShutDown &evt) override;
        void handle(const bluetooth::event::Pair &evt) override;
        void handle(const bluetooth::event::Unpair &evt) override;
        void handle(const bluetooth::event::StartRinging &evt) override;
        void handle(const bluetooth::event::StopRinging &evt) override;
        void handle(const bluetooth::event::StartRouting &evt) override;
        void handle(const bluetooth::event::StartStream &evt) override;
        void handle(const bluetooth::event::StopStream &evt) override;
        void handle(const bluetooth::event::CallAnswered &evt) override;
        void handle(const bluetooth::event::CallTerminated &evt) override;
        void handle(const bluetooth::event::CallStarted &evt) override;
        void handle(const bluetooth::event::CallMissed &evt) override;
        void handle(const bluetooth::event::OutgoingCallStarted &evt) override;
        void handle(const bluetooth::event::IncomingCallStarted &evt) override;
        void handle(const bluetooth::event::IncomingCallNumber &evt) override;
        void handle(const bluetooth::event::SignalStrengthData &evt) override;
        void handle(const bluetooth::event::OperatorNameData &evt) override;

M module-bluetooth/Bluetooth/command/event/Events.hpp => module-bluetooth/Bluetooth/command/event/Events.hpp +82 -68
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 13,7 13,7 @@ namespace bluetooth::event
{
    struct Base
    {
        virtual void dispatch(bluetooth::AbstractController *controler) const = 0;
        virtual void dispatch(bluetooth::AbstractController *controller) const = 0;
        virtual ~Base()                                                       = default;

      protected:


@@ 22,210 22,224 @@ namespace bluetooth::event

    struct StartScan : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct StopScan : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct GetDevicesAvailable : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct VisibilityOn : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct VisibilityOff : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct Connect : public Base
    {
        explicit Connect(const Devicei &dev) : device(dev)
        {}
        const Devicei device;

        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct Disconnect : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct PowerOn : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct PowerOff : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct ShutDown : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct Pair : public Base
    {
        Pair(const Devicei &device) : device(device)
        explicit Pair(const Devicei &device) : device(device)
        {}
        const Devicei device;

        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct Unpair : public Base
    {
        explicit Unpair(const Devicei &device) : device(device)
        {}
        const Devicei device;

        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct StartRinging : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct StopRinging : public Base
    struct StartStream : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct StartRouting : public Base

    struct StopStream : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct StartStream : public Base

    struct CallAnswered : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct StopStream : public Base

    struct CallTerminated : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct CallAnswered : public Base

    struct CallMissed : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct CallTerminated : public Base

    struct OutgoingCallStarted : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        explicit OutgoingCallStarted(const utils::PhoneNumber &number) : number(number)
        {}
        const utils::PhoneNumber number;
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
    struct CallStarted : public Base

    struct IncomingCallStarted : public Base
    {
        explicit CallStarted(const utils::PhoneNumber &number) : number(number)
        {}
        const utils::PhoneNumber number;
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct IncomingCallNumber : public Base
    {
        explicit IncomingCallNumber(utils::PhoneNumber::View &number) : number(number)
        {}
        const utils::PhoneNumber::View number;
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct SignalStrengthData : public Base
    {
        explicit SignalStrengthData(const Store::SignalStrength &strength) : strength(strength)
        {}
        Store::SignalStrength strength;
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct OperatorNameData : public Base
    {
        explicit OperatorNameData(const std::string &name) : name(name)
        {}
        const std::string name;
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct BatteryLevelData : public Base
    {
        explicit BatteryLevelData(unsigned int level) : level(level)
        {}
        const unsigned int level;
        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };

    struct NetworkStatusData : public Base
    {
        explicit NetworkStatusData(Store::Network::Status status) : status(status)
        {}
        const Store::Network::Status status;

        void dispatch(bluetooth::AbstractController *controler) const override
        void dispatch(bluetooth::AbstractController *controller) const override
        {
            controler->handle(*this);
            controller->handle(*this);
        }
    };
} // namespace bluetooth::event

M module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp => module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp +6 -6
@@ 1,11 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <functional>
#include "Device.hpp"
#include "Error.hpp"
#include "Result.hpp"

namespace bluetooth
{


@@ 17,10 17,10 @@ namespace bluetooth
        virtual ~AbstractDriver() noexcept = default;
        using ErrorCallback                = std::function<void(uint8_t)>;

        [[nodiscard]] virtual auto init() -> Error::Code                    = 0;
        [[nodiscard]] virtual auto run() -> Error::Code                     = 0;
        [[nodiscard]] virtual auto stop() -> Error::Code                    = 0;
        [[nodiscard]] virtual auto scan() -> Error                          = 0;
        [[nodiscard]] virtual auto init() -> Result::Code                   = 0;
        [[nodiscard]] virtual auto run() -> Result::Code                    = 0;
        [[nodiscard]] virtual auto stop() -> Result::Code                   = 0;
        [[nodiscard]] virtual auto scan() -> Result                         = 0;
        virtual void stopScan()                                             = 0;
        virtual void setVisibility(bool visibility)                         = 0;
        virtual void pair(Devicei device, std::uint8_t protectionLevel = 0) = 0;

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp +9 -9
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BluetoothDriverImpl.hpp"


@@ 60,7 60,7 @@ namespace bluetooth
        : runLoop{runLoop}, gap{std::make_unique<bluetooth::GAP>(ownerService)}
    {}

    auto Driver::init() -> Error::Code
    auto Driver::init() -> Result::Code
    {
        btstack_memory_init();
        config = {


@@ 88,7 88,7 @@ namespace bluetooth

        gap_set_class_of_device(0x64020C);
        LOG_DEBUG("BT worker run success");
        return Error::Success;
        return Result::Code::Success;
    }

    void Driver::hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)


@@ 167,16 167,16 @@ namespace bluetooth
            break;
        }
    }
    auto Driver::run() -> Error::Code
    auto Driver::run() -> Result::Code
    {
        auto ret = hci_power_control(HCI_POWER_ON);
        if (ret != 0) {
            LOG_ERROR("HCI power on failed, can't start Bluetooth!");
            return Error::LibraryError;
            return Result::Code::LibraryError;
        }
        LOG_INFO("HCI turned on - run BtStack loop\n");
        btstack_run_loop_execute();
        return Error::Success;
        return Result::Code::Success;
    }

    void Driver::registerErrorCallback(const ErrorCallback &newCallback)


@@ 196,7 196,7 @@ namespace bluetooth
        powerOnCallback = newCallback;
    }

    auto Driver::stop() -> Error::Code
    auto Driver::stop() -> Result::Code
    {
        auto ret = hci_power_control(HCI_POWER_OFF);
        if (ret != 0) {


@@ 204,9 204,9 @@ namespace bluetooth
        }
        bluetooth::KeyStorage::settings->setValue(bluetooth::Settings::State,
                                                  static_cast<int>(BluetoothStatus::State::Off));
        return ret != 0 ? Error::LibraryError : Error::Success;
        return ret != 0 ? Result::Code::LibraryError : Result::Code::Success;
    }
    auto Driver::scan() -> Error
    auto Driver::scan() -> Result
    {
        return gap->scan();
    }

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp +6 -6
@@ 1,9 1,10 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "BluetoothDriver.hpp"
#include "GAP/GAP.hpp"

extern "C"
{


@@ 11,7 12,6 @@ extern "C"
#include <hci_transport.h>
#include <hci_transport_h4.h>
}
#include "GAP/GAP.hpp"

namespace bluetooth
{


@@ 35,13 35,13 @@ namespace bluetooth
      public:
        Driver(const btstack_run_loop *runLoop, sys::Service *ownerService);

        [[nodiscard]] auto init() -> Error::Code override;
        [[nodiscard]] auto run() -> Error::Code override;
        [[nodiscard]] auto stop() -> Error::Code override;
        [[nodiscard]] auto init() -> Result::Code override;
        [[nodiscard]] auto run() -> Result::Code override;
        [[nodiscard]] auto stop() -> Result::Code override;
        void registerErrorCallback(const ErrorCallback &newCallback) override;
        void registerPowerOnCallback(const PowerOnCallback &newCallback) override;

        auto scan() -> Error override;
        auto scan() -> Result override;
        void stopScan() override;
        void setVisibility(bool visibility) override;
        void pair(Devicei device, std::uint8_t protectionLevel = 0) override;

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +177 -175
@@ 1,16 1,12 @@
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

//
// Created by bartek on 14.09.2020.
//

#include "A2DP.hpp"
#include "A2DPImpl.hpp"
#include "AVDTP.hpp"
#include "AVRCP.hpp"
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <Bluetooth/Result.hpp>
#include <log/log.hpp>
#include <Audio/AudioCommon.hpp>
#include <service-audio/AudioMessage.hpp>


@@ 26,6 22,8 @@ extern "C"
#include <btstack_defines.h>
}

#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))

namespace bluetooth
{
    A2DP::A2DP() : pimpl(std::make_unique<A2DPImpl>(A2DPImpl()))


@@ 53,8 51,9 @@ namespace bluetooth

    A2DP &A2DP::operator=(A2DP &&other) noexcept
    {
        if (&other == this)
        if (&other == this) {
            return *this;
        }

        pimpl       = std::move(other.pimpl);
        other.pimpl = nullptr;


@@ 62,7 61,7 @@ namespace bluetooth
        return *this;
    }

    auto A2DP::init() -> Error::Code
    auto A2DP::init() -> Result::Code
    {
        return pimpl->init();
    }


@@ 72,7 71,7 @@ namespace bluetooth
        pimpl->setDevice(device);
    }

    void A2DP::setOwnerService(const sys::Service *service)
    void A2DP::setOwnerService(sys::Service *service)
    {
        pimpl->setOwnerService(service);
    }


@@ 102,119 101,116 @@ namespace bluetooth
        pimpl->setAudioDevice(std::move(audioDevice));
    }

    const sys::Service *A2DP::A2DPImpl::ownerService;
    QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr;
    QueueHandle_t A2DP::A2DPImpl::sinkQueue   = nullptr;
    DeviceMetadata_t A2DP::A2DPImpl::metadata;
    btstack_packet_callback_registration_t A2DP::A2DPImpl::hciEventCallbackRegistration;
    std::array<std::uint8_t, A2DP::A2DPImpl::SDP_BUFFER_LENGTH> A2DP::A2DPImpl::sdpSourceServiceBuffer;
    std::array<std::uint8_t, A2DP::A2DPImpl::MEDIA_CAP_SIZE> A2DP::A2DPImpl::mediaSbcCodecCapabilities = {
    std::uint8_t A2DP::A2DPImpl::sdpSourceServiceBuffer[sourceServiceBufferSize];
    std::uint8_t A2DP::A2DPImpl::mediaSbcCodecCapabilities[] = {
        (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
        0xFF, //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
        2,
        53};

    std::shared_ptr<BluetoothAudioDevice> A2DP::A2DPImpl::audioDevice;
    sys::Service *A2DP::A2DPImpl::ownerService = nullptr;
    Devicei A2DP::A2DPImpl::device;

    btstack_packet_callback_registration_t A2DP::A2DPImpl::hciEventCallbackRegistration;

    QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr;

    bool A2DP::A2DPImpl::isConnected = false;
    Devicei A2DP::A2DPImpl::device;
    /* LISTING_START(MainConfiguration): Setup Audio Source and AVRCP Target services */

    auto A2DP::A2DPImpl::init() -> Error::Code
    auto A2DP::A2DPImpl::init() -> Result::Code
    {
        // request role change on reconnecting headset to always use them in slave mode
        hci_set_master_slave_policy(0);

        Profile::initL2cap();
        // Initialize  A2DP Source.

        a2dp_source_init();
        a2dp_source_register_packet_handler(&sourcePacketHandler);

        // Create stream endpoint.
        avdtp_stream_endpoint_t *local_stream_endpoint =
            a2dp_source_create_stream_endpoint(AVDTP_AUDIO,
                                               AVDTP_CODEC_SBC,
                                               mediaSbcCodecCapabilities.data(),
                                               mediaSbcCodecCapabilities.size(),
                                               AVDTP::sbcCodecConfiguration.data(),
                                               AVDTP::sbcCodecConfiguration.size());
        const auto local_stream_endpoint = a2dp_source_create_stream_endpoint(AVDTP_AUDIO,
                                                                              AVDTP_CODEC_SBC,
                                                                              mediaSbcCodecCapabilities,
                                                                              ARRAY_LENGTH(mediaSbcCodecCapabilities),
                                                                              AVDTP::sbcCodecConfiguration.data(),
                                                                              AVDTP::sbcCodecConfiguration.size());
        if (local_stream_endpoint == nullptr) {
            LOG_INFO("A2DP Source: not enough memory to create local stream endpoint");
            return bluetooth::Error::SystemError;
            return Result::Code::SystemError;
        }
        AVRCP::mediaTracker.local_seid = avdtp_local_seid(local_stream_endpoint);
        avdtp_source_register_delay_reporting_category(AVRCP::mediaTracker.local_seid);

        AVRCP::init(const_cast<sys::Service *>(ownerService));
        AVRCP::init(ownerService);

        // Initialize SDP
        Profile::initSdp();

        // Create  A2DP Source service record and register it with SDP.
        sdpSourceServiceBuffer.fill(0);
        std::memset(sdpSourceServiceBuffer, 0, sizeof(sdpSourceServiceBuffer));

        a2dp_source_create_sdp_record(
            sdpSourceServiceBuffer.data(), a2dpSdpRecordHandle, AVDTP_SOURCE_FEATURE_MASK_PLAYER, nullptr, nullptr);
        if (const auto status = sdp_register_service(sdpSourceServiceBuffer.data()); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service. Status %x", status);
            sdpSourceServiceBuffer, a2dpSdpRecordHandle, AVDTP_SOURCE_FEATURE_MASK_PLAYER, nullptr, nullptr);
        if (const auto status = sdp_register_service(sdpSourceServiceBuffer); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service, status 0x%02X", status);
        }

        // Create AVRCP target service record and register it with SDP.
        AVRCP::sdpTargetServiceBuffer.fill(0);
        std::memset(AVRCP::sdpTargetServiceBuffer, 0, sizeof(AVRCP::sdpTargetServiceBuffer));

        std::uint16_t supportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
#ifdef AVRCP_BROWSING_ENABLED
        supported_features |= AVRCP_FEATURE_MASK_BROWSING;
#endif
        avrcp_target_create_sdp_record(
            AVRCP::sdpTargetServiceBuffer.data(), avrcpServiceSdpRecordHandle, supportedFeatures, nullptr, nullptr);
        if (const auto status = sdp_register_service(AVRCP::sdpTargetServiceBuffer.data());
            status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service. Status %x", status);
            AVRCP::sdpTargetServiceBuffer, avrcpServiceSdpRecordHandle, supportedFeatures, nullptr, nullptr);
        if (const auto status = sdp_register_service(AVRCP::sdpTargetServiceBuffer); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service, status 0x%02X", status);
        }

        // setup AVRCP Controller
        AVRCP::sdpControllerServiceBuffer.fill(0);
        std::uint16_t controllerSupportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_MONITOR_OR_AMPLIFIER;
        avrcp_controller_create_sdp_record(AVRCP::sdpControllerServiceBuffer.data(),
        std::memset(AVRCP::sdpControllerServiceBuffer, 0, sizeof(AVRCP::sdpControllerServiceBuffer));

        const std::uint16_t controllerSupportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_MONITOR_OR_AMPLIFIER;

        avrcp_controller_create_sdp_record(AVRCP::sdpControllerServiceBuffer,
                                           avrcpControllerSdpRecordHandle,
                                           controllerSupportedFeatures,
                                           nullptr,
                                           nullptr);
        if (const auto status = sdp_register_service(AVRCP::sdpControllerServiceBuffer.data());
            status != ERROR_CODE_SUCCESS) {
        if (const auto status = sdp_register_service(AVRCP::sdpControllerServiceBuffer); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service. Status %x", status);
        }

        // Register for HCI events.
        hciEventCallbackRegistration.callback = &hciPacketHandler;
        hci_add_event_handler(&hciEventCallbackRegistration);

        LOG_INFO("Init done!");

        return bluetooth::Error::Success;
        LOG_INFO("A2DP initialized!");
        return Result::Code::Success;
    }

    void A2DP::A2DPImpl::sendMediaPacket()
    {
        int numBytesInFrame    = btstack_sbc_encoder_sbc_buffer_length();
        int bytesInStorage     = AVRCP::mediaTracker.sbc_storage_count;
        std::uint8_t numFrames = bytesInStorage / numBytesInFrame;
        const auto numBytesInFrame   = btstack_sbc_encoder_sbc_buffer_length();
        const auto bytesInStorage    = AVRCP::mediaTracker.sbc_storage_count;
        const std::uint8_t numFrames = bytesInStorage / numBytesInFrame;

        a2dp_source_stream_send_media_payload(AVRCP::mediaTracker.a2dp_cid,
                                              AVRCP::mediaTracker.local_seid,
                                              AVRCP::mediaTracker.sbc_storage,
                                              bytesInStorage,
                                              numFrames,
                                              0);

        AVRCP::mediaTracker.sbc_storage_count = 0;
        AVRCP::mediaTracker.sbc_ready_to_send = 0;
    }

    void A2DP::A2DPImpl::audioTimeoutHandler(btstack_timer_source_t *timer)
    {
        auto *context = static_cast<MediaContext *>(btstack_run_loop_get_timer_context(timer));
        btstack_run_loop_set_timer(&context->audio_timer, AUDIO_TIMEOUT_MS);
        const auto context = static_cast<MediaContext *>(btstack_run_loop_get_timer_context(timer));
        btstack_run_loop_set_timer(&context->audio_timer, audioTimeoutMs);
        btstack_run_loop_add_timer(&context->audio_timer);
        std::uint32_t now = btstack_run_loop_get_time_ms();
        const auto now = btstack_run_loop_get_time_ms();

        std::uint32_t updatePeriodMs = AUDIO_TIMEOUT_MS;
        std::uint32_t updatePeriodMs = audioTimeoutMs;
        if (context->time_audio_data_sent_in_ms > 0) {
            updatePeriodMs = now - context->time_audio_data_sent_in_ms;
        }


@@ 229,7 225,7 @@ namespace bluetooth
        context->time_audio_data_sent_in_ms = now;
        context->samples_ready += numSamples;

        if (context->sbc_ready_to_send != 0u) {
        if (context->sbc_ready_to_send != 0) {
            return;
        }



@@ 238,31 234,31 @@ namespace bluetooth
        }

        if ((context->sbc_storage_count + btstack_sbc_encoder_sbc_buffer_length()) > context->max_media_payload_size) {
            // schedule sending
            context->sbc_ready_to_send = 1;
            context->sbc_ready_to_send = 1; // schedule sending
            a2dp_source_stream_endpoint_request_can_send_now(context->a2dp_cid, context->local_seid);
        }
    }

    void A2DP::A2DPImpl::startTimer(MediaContext *context)
    {
        LOG_DEBUG("Timer start");
        LOG_DEBUG("A2DP timer start");

        context->max_media_payload_size =
            btstack_min(a2dp_max_media_payload_size(context->a2dp_cid, context->local_seid), SBC_STORAGE_SIZE);
        context->sbc_storage_count = 0;
        context->sbc_ready_to_send = 0;
        context->streaming         = 1;

        btstack_run_loop_remove_timer(&context->audio_timer);
        btstack_run_loop_set_timer_handler(&context->audio_timer, audioTimeoutHandler);
        btstack_run_loop_set_timer_context(&context->audio_timer, context);
        btstack_run_loop_set_timer(&context->audio_timer, A2DP::A2DPImpl::AUDIO_TIMEOUT_MS);
        btstack_run_loop_set_timer(&context->audio_timer, audioTimeoutMs);
        btstack_run_loop_add_timer(&context->audio_timer);
    }

    void A2DP::A2DPImpl::stopTimer(MediaContext *context)
    {
        LOG_DEBUG("Timer stop");
        LOG_DEBUG("A2DP timer stop");

        context->time_audio_data_sent_in_ms = 0;
        context->acc_num_missed_samples     = 0;


@@ 270,18 266,15 @@ namespace bluetooth
        context->streaming                  = 1;
        context->sbc_storage_count          = 0;
        context->sbc_ready_to_send          = 0;

        btstack_run_loop_remove_timer(&context->audio_timer);
    }

    void A2DP::A2DPImpl::hciPacketHandler(uint8_t packetType,
    void A2DP::A2DPImpl::hciPacketHandler([[maybe_unused]] uint8_t packetType,
                                          [[maybe_unused]] uint16_t channel,
                                          [[maybe_unused]] uint8_t *packet,
                                          [[maybe_unused]] uint16_t size)
    {
        if (packetType != HCI_EVENT_PACKET) {
            return;
        }
    }
    {}

    void A2DP::A2DPImpl::sourcePacketHandler(uint8_t packetType,
                                             [[maybe_unused]] uint16_t channel,


@@ 296,6 289,7 @@ namespace bluetooth
        if (packetType != HCI_EVENT_PACKET) {
            return;
        }

        if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) {
            return;
        }


@@ 307,38 301,41 @@ namespace bluetooth
            status = a2dp_subevent_signaling_connection_established_get_status(packet);

            if (status != ERROR_CODE_SUCCESS) {
                LOG_INFO("A2DP Source: Connection failed, status 0x%02x, cid 0x%02x, a2dp_cid 0x%02x ",
                LOG_INFO("Connection failed, status 0x%02X, cid 0x%02X, a2dp_cid 0x%02X",
                         status,
                         cid,
                         AVRCP::mediaTracker.a2dp_cid);
                AVRCP::mediaTracker.a2dp_cid = 0;

                auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, false),
                                     service::name::bluetooth);
                ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(
                                                  device, message::bluetooth::ConnectResult::Result::Failure),
                                              service::name::bluetooth);
                break;
            }

            AVRCP::mediaTracker.a2dp_cid = cid;
            AVRCP::mediaTracker.volume   = 64;

            LOG_INFO("A2DP Source: Connected, a2dp cid 0x%02x, local seid %d",
            LOG_INFO("Connected, a2dp_cid 0x%02X, local seid 0x%02X",
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid);
            isConnected        = true;
            auto &busProxy     = const_cast<sys::Service *>(ownerService)->bus;
            device.deviceState = DeviceState::ConnectedAudio;
            // handle proper device matching when connection was initiated by remote device
            isConnected        = true;

            /* Handle proper device matching when connection was initiated by remote device */
            a2dp_subevent_signaling_connection_established_get_bd_addr(packet, device.address);

            busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, true),
                                 service::name::bluetooth);
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(
                                              device, message::bluetooth::ConnectResult::Result::Success),
                                          service::name::bluetooth);
            break;
        }

        case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: {
            cid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_avdtp_cid(packet);
            if (cid != AVRCP::mediaTracker.a2dp_cid) {
                return;
            }

            AVRCP::mediaTracker.remote_seid =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_remote_seid(packet);



@@ 355,31 352,35 @@ namespace bluetooth
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(packet);
            AVDTP::sbcConfig.maxBitpoolValue =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet);
            LOG_INFO("A2DP Source: Received SBC codec configuration, sampling frequency %u, a2dp_cid 0x%02x, local "
                     "seid 0x%02x, remote seid 0x%02x",

            LOG_INFO("Received SBC codec configuration, sampling frequency %uHz, a2dp_cid 0x%02X, local seid 0x%02X, "
                     "remote seid 0x%02X",
                     AVDTP::sbcConfig.samplingFrequency,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     AVRCP::mediaTracker.remote_seid);

            // Adapt Bluetooth spec definition to SBC Encoder expected input
            auto allocation_method =
            const auto allocation_method =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(packet);
            AVDTP::sbcConfig.allocationMethod = static_cast<btstack_sbc_allocation_method_t>(allocation_method - 1);
            auto channel_mode                 = static_cast<avdtp_channel_mode_t>(
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(packet));

            AVDTP::sbcConfig.numChannels = 2;

            const auto channel_mode = static_cast<avdtp_channel_mode_t>(
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(packet));
            switch (channel_mode) {
            case AVDTP_CHANNEL_MODE_JOINT_STEREO:
                AVDTP::sbcConfig.channelMode = SBC_CHANNEL_MODE_JOINT_STEREO;
                break;

            case AVDTP_CHANNEL_MODE_STEREO:
                AVDTP::sbcConfig.channelMode = SBC_CHANNEL_MODE_STEREO;
                break;

            case AVDTP_CHANNEL_MODE_DUAL_CHANNEL:
                AVDTP::sbcConfig.channelMode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
                break;

            case AVDTP_CHANNEL_MODE_MONO:
                AVDTP::sbcConfig.channelMode = SBC_CHANNEL_MODE_MONO;
                break;


@@ 394,165 395,165 @@ namespace bluetooth
                                     AVDTP::sbcConfig.samplingFrequency,
                                     AVDTP::sbcConfig.maxBitpoolValue,
                                     AVDTP::sbcConfig.channelMode);
        } break;

            metadata = DeviceMetadata_t{
                .sampleRate      = static_cast<unsigned int>(AVDTP::sbcConfig.samplingFrequency),
                .channels        = static_cast<unsigned short>(AVDTP::sbcConfig.numChannels),
                .samplesPerFrame = static_cast<unsigned int>(btstack_sbc_encoder_num_audio_frames()),
            };

            break;
        }

        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY:
            LOG_INFO("A2DP Source: remote supports delay report, remote seid %d",
        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY: {
            LOG_INFO("Remote supports delay report, remote seid 0x%02X",
                     avdtp_subevent_signaling_delay_reporting_capability_get_remote_seid(packet));
            break;
        case A2DP_SUBEVENT_SIGNALING_CAPABILITIES_DONE:
            LOG_INFO("A2DP Source: All capabilities reported, remote seid %d",
        } break;

        case A2DP_SUBEVENT_SIGNALING_CAPABILITIES_DONE: {
            LOG_INFO("All capabilities reported, remote seid 0x%02X",
                     avdtp_subevent_signaling_capabilities_done_get_remote_seid(packet));
            break;
        } break;

        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORT:
            LOG_INFO("A2DP Source: Received delay report of %d.%0d ms, local seid %d",
        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORT: {
            LOG_INFO("Received delay report of %d.%0d ms, local seid 0x%02X",
                     avdtp_subevent_signaling_delay_report_get_delay_100us(packet) / 10,
                     avdtp_subevent_signaling_delay_report_get_delay_100us(packet) % 10,
                     avdtp_subevent_signaling_delay_report_get_local_seid(packet));
            break;
        } break;

        case A2DP_SUBEVENT_STREAM_ESTABLISHED:
        case A2DP_SUBEVENT_STREAM_ESTABLISHED: {
            a2dp_subevent_stream_established_get_bd_addr(packet, address);
            status = a2dp_subevent_stream_established_get_status(packet);
            if (status != 0u) {
                LOG_INFO("A2DP Source: Stream failed, status 0x%02x", status);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_INFO("Stream failed, status 0x%02X", status);
                break;
            }

            local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
            cid        = a2dp_subevent_stream_established_get_a2dp_cid(packet);
            LOG_INFO("A2DP_SUBEVENT_STREAM_ESTABLISHED:  a2dp_cid [expected 0x%02x, received 0x%02x], local_seid %d "
                     "(expected %d), remote_seid %d (expected %d)",

            LOG_INFO("A2DP_SUBEVENT_STREAM_ESTABLISHED: a2dp_cid [expected 0x%02X, received 0x%02X], local_seid "
                     "[expected 0x%02X, received 0x%02X], remote_seid [expected 0x%02X, received 0x%02X]",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     local_seid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid,
                     a2dp_subevent_stream_established_get_remote_seid(packet),
                     AVRCP::mediaTracker.remote_seid);

            if (local_seid != AVRCP::mediaTracker.local_seid) {
                LOG_INFO("A2DP Source: Stream failed, wrong local seid %d, expected %d",
                LOG_INFO("Stream failed, wrong local seid 0x%02X, expected 0x%02X",
                         local_seid,
                         AVRCP::mediaTracker.local_seid);
                break;
            }
            LOG_INFO("A2DP Source: Stream established, a2dp cid 0x%02x, local seid %d, remote seid %d",

            LOG_INFO("Stream established, a2dp_cid 0x%02X, local seid 0x%02X, remote seid 0x%02X",
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid,
                     a2dp_subevent_stream_established_get_remote_seid(packet));

            sourceQueue = xQueueCreate(5, sizeof(AudioData_t));
            if (sourceQueue != nullptr) {
                sendAudioEvent(audio::EventType::BluetoothA2DPDeviceState, audio::Event::DeviceState::Connected);
            }
            else {
                LOG_ERROR("failed to create queue!");
            sourceQueue = xQueueCreate(sourceQueueLength, sizeof(AudioData_t));
            if (sourceQueue == nullptr) {
                LOG_ERROR("Failed to create sourceQueue!");
                break;
            }

            break;
            sendAudioEvent(audio::EventType::BluetoothA2DPDeviceState, audio::Event::DeviceState::Connected);
        } break;

        case A2DP_SUBEVENT_STREAM_RECONFIGURED:
            status     = a2dp_subevent_stream_reconfigured_get_status(packet);
        case A2DP_SUBEVENT_STREAM_RECONFIGURED: {
            local_seid = a2dp_subevent_stream_reconfigured_get_local_seid(packet);
            cid        = a2dp_subevent_stream_reconfigured_get_a2dp_cid(packet);
            status     = a2dp_subevent_stream_reconfigured_get_status(packet);

            LOG_INFO("A2DP Source: Reconfigured: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                     "received %d]",
            LOG_INFO("Reconfigured, a2dp_cid [expected 0x%02X, received 0x%02X], local_seid [expected 0x%02X, received "
                     "0x%02X], status 0x%02X",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid);
            LOG_INFO("Status 0x%02x", status);
            break;
                     local_seid,
                     status);
        } break;

        case A2DP_SUBEVENT_STREAM_STARTED:
        case A2DP_SUBEVENT_STREAM_STARTED: {
            local_seid = a2dp_subevent_stream_started_get_local_seid(packet);
            cid        = a2dp_subevent_stream_started_get_a2dp_cid(packet);

            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PLAYING;
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {
            if (AVRCP::mediaTracker.avrcp_cid != 0) {
                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP::playInfo.status);
            }
            startTimer(&AVRCP::mediaTracker);
            LOG_INFO(
                "A2DP Source: Stream started: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);
            break;

        case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW:
            local_seid = a2dp_subevent_streaming_can_send_media_packet_now_get_local_seid(packet);
            cid        = a2dp_subevent_signaling_media_codec_sbc_configuration_get_a2dp_cid(packet);
            LOG_INFO("Stream started: a2dp_cid [expected 0x%02X, received 0x%02X], local_seid [expected 0x%02X, "
                     "received 0x%02X]",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid);
        } break;

        case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW: {
            sendMediaPacket();
            break;
        } break;

        case A2DP_SUBEVENT_STREAM_SUSPENDED:
        case A2DP_SUBEVENT_STREAM_SUSPENDED: {
            local_seid = a2dp_subevent_stream_suspended_get_local_seid(packet);
            cid        = a2dp_subevent_stream_suspended_get_a2dp_cid(packet);

            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PAUSED;
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {
            if (AVRCP::mediaTracker.avrcp_cid != 0) {
                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP::playInfo.status);
            }
            LOG_INFO(
                "A2DP Source: Stream paused: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);

            LOG_INFO("Stream paused: a2dp_cid [expected 0x%02X, received 0x%02X], local_seid [expected 0x%02X, "
                     "received 0x%02X]",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid);

            stopTimer(&AVRCP::mediaTracker);
            break;
        } break;

        case A2DP_SUBEVENT_STREAM_RELEASED: {
            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_STOPPED;
            cid                    = a2dp_subevent_stream_released_get_a2dp_cid(packet);
            local_seid             = a2dp_subevent_stream_released_get_local_seid(packet);
            LOG_INFO(
                "A2DP Source: Stream released: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);

            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_STOPPED;

            LOG_INFO("Stream released: a2dp_cid [expected 0x%02X, received 0x%02X], local_seid [expected 0x%02X, "
                     "received 0x%02X]",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid);

            sendAudioEvent(audio::EventType::BluetoothA2DPDeviceState, audio::Event::DeviceState::Disconnected);

            if (cid == AVRCP::mediaTracker.a2dp_cid) {
                AVRCP::mediaTracker.stream_opened = 0;
                LOG_INFO("A2DP Source: Stream released");
                LOG_INFO("Stream released");
            }
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {

            if (AVRCP::mediaTracker.avrcp_cid != 0) {
                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_STOPPED);
            }
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                 service::name::bluetooth);

            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                          service::name::bluetooth);

            stopTimer(&AVRCP::mediaTracker);
            break;
        }
        case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
        case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED: {
            cid = a2dp_subevent_signaling_connection_released_get_a2dp_cid(packet);

            if (cid == AVRCP::mediaTracker.a2dp_cid) {
                AVRCP::mediaTracker.avrcp_cid = 0;
                AVRCP::mediaTracker.a2dp_cid  = 0;
                LOG_INFO("A2DP Source: Signaling released");
                LOG_INFO("Signaling connection released");
            }

            isConnected = false;
            break;
        } break;

        default:
            LOG_DEBUG("Event not handled: 0x%02X", hci_event_a2dp_meta_get_subevent_code(packet));
            break;
        }
    }


@@ 562,6 563,7 @@ namespace bluetooth
        if (isConnected) {
            disconnect();
        }

        LOG_INFO("Connecting A2DP profile");
        a2dp_source_establish_stream(device.address, &AVRCP::mediaTracker.a2dp_cid);
    }


@@ 570,8 572,8 @@ namespace bluetooth
    {
        LOG_INFO("Disconnecting A2DP profile");
        a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device), service::name::bluetooth);
        ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                      service::name::bluetooth);
    }

    void A2DP::A2DPImpl::setDevice(const Devicei &dev)


@@ 580,7 582,7 @@ namespace bluetooth
        LOG_INFO("Device set!");
    }

    void A2DP::A2DPImpl::setOwnerService(const sys::Service *service)
    void A2DP::A2DPImpl::setOwnerService(sys::Service *service)
    {
        ownerService = service;
    }


@@ 589,9 591,9 @@ namespace bluetooth
    {
        auto evt       = std::make_shared<audio::Event>(event, state);
        auto msg       = std::make_shared<AudioEventRequest>(std::move(evt));
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
        ownerService->bus.sendUnicast(std::move(msg), service::name::evt_manager);
    }

    void A2DP::A2DPImpl::start()
    {
        if (!isConnected) {


@@ 600,6 602,7 @@ namespace bluetooth
        AVRCP::mediaTracker.stream_opened = 1;
        a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
    }

    void A2DP::A2DPImpl::stop()
    {
        AVRCP::mediaTracker.stream_opened = 1;


@@ 610,15 613,14 @@ namespace bluetooth
    {
        A2DP::A2DPImpl::audioDevice = std::move(newAudioDevice);
    }

    void A2DP::A2DPImpl::deInit()
    {
        LOG_DEBUG("Deinitializing A2DP profile");

        sdp_unregister_service(a2dpSdpRecordHandle);
        sdp_unregister_service(avrcpControllerSdpRecordHandle);
        sdp_unregister_service(avrcpServiceSdpRecordHandle);

        audioDevice.reset();
    }

        LOG_DEBUG("A2DP deinitialized!");
    }
} // namespace bluetooth

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

#pragma once


@@ 22,9 22,9 @@ namespace bluetooth
        A2DP(A2DP &&other) noexcept;
        auto operator=(A2DP &&other) noexcept -> A2DP &;

        auto init() -> Error::Code override;
        auto init() -> Result::Code override;
        void setDevice(const Devicei &device) override;
        void setOwnerService(const sys::Service *service) override;
        void setOwnerService(sys::Service *service) override;

        void connect() override;
        void disconnect() override;

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +39 -35
@@ 1,16 1,15 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "A2DP.hpp"
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <Bluetooth/Result.hpp>
#include <log/log.hpp>
#include <Audio/AudioCommon.hpp>
#include <audio/BluetoothAudioDevice.hpp>

#include "MediaContext.hpp"

#include <memory>

extern "C"


@@ 24,45 23,50 @@ namespace bluetooth
    class AVRCP;
    class A2DP::A2DPImpl
    {
      public:
        auto init() -> Result::Code;
        void deInit();
        void connect();
        void disconnect();
        void start();
        void stop();
        void setDevice(const Devicei &dev);
        void setOwnerService(sys::Service *service);
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);

        static constexpr int NUM_CHANNELS           = 2;
        static constexpr int BYTES_PER_AUDIO_SAMPLE = (2 * NUM_CHANNELS);
        static constexpr int AUDIO_TIMEOUT_MS       = 10;
        static constexpr int TABLE_SIZE_441HZ       = 100;
        static constexpr int SDP_BUFFER_LENGTH      = 150;
        static constexpr int MEDIA_CAP_SIZE         = 4;

        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpSourceServiceBuffer;
        static btstack_packet_callback_registration_t hciEventCallbackRegistration;
        static std::array<uint8_t, MEDIA_CAP_SIZE> mediaSbcCodecCapabilities;
        static const sys::Service *ownerService;
        static QueueHandle_t sourceQueue;
        static QueueHandle_t sinkQueue;
        static DeviceMetadata_t metadata;

      private:
        static void startTimer(MediaContext *context);
        static void stopTimer(MediaContext *context);
        static void audioTimeoutHandler(btstack_timer_source_t *timer);
        static void sourcePacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void hciPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void sourcePacketHandler(std::uint8_t packetType,
                                        std::uint16_t channel,
                                        std::uint8_t *packet,
                                        std::uint16_t size);
        static void hciPacketHandler(std::uint8_t packetType,
                                     std::uint16_t channel,
                                     std::uint8_t *packet,
                                     std::uint16_t size);
        static void sendMediaPacket();
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;

        static constexpr std::uint32_t a2dpSdpRecordHandle            = 0x10001;
        static constexpr std::uint32_t avrcpServiceSdpRecordHandle    = 0x10002;
        static constexpr std::uint32_t avrcpControllerSdpRecordHandle = 0x10003;
        static constexpr auto sourceServiceBufferSize                 = 150;
        static constexpr auto sourceQueueLength                       = 5;
        static constexpr int audioTimeoutMs                           = 10;

        static std::uint8_t sdpSourceServiceBuffer[sourceServiceBufferSize];
        static std::uint8_t mediaSbcCodecCapabilities[];

        static std::shared_ptr<BluetoothAudioDevice> audioDevice;
        static sys::Service *ownerService;
        static Devicei device;
        static constexpr uint32_t a2dpSdpRecordHandle            = 0x10001;
        static constexpr uint32_t avrcpServiceSdpRecordHandle    = 0x10002;
        static constexpr uint32_t avrcpControllerSdpRecordHandle = 0x10003;

      public:
        auto init() -> Error::Code;
        void deInit();
        void connect();
        void disconnect();
        void start();
        void stop();
        void setDevice(const Devicei &dev);
        void setOwnerService(const sys::Service *service);
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);
        static btstack_packet_callback_registration_t hciEventCallbackRegistration;

        static QueueHandle_t sourceQueue;

        static bool isConnected;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AVDTP.hpp"


@@ 6,8 6,8 @@
namespace bluetooth
{
    AVDTP::SbcConfiguration AVDTP::sbcConfig;
    std::array<std::uint8_t, AVDTP::sbcCodecConfigurationSize> AVDTP::sbcCodecConfiguration;
    btstack_sbc_encoder_state_t AVDTP::sbcEncoderState;
    std::array<uint8_t, 4> AVDTP::sbcCodecConfiguration;
    int AVDTP::sampleRate = AVDTP::defaultSampleRate;

    void AVDTP::dumpSbcConfiguration()

M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp +10 -7
@@ 1,25 1,25 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "A2DPImpl.hpp"

extern "C"
{
#include <btstack.h>
#include <btstack_defines.h>
#include <classic/avdtp.h>
}

namespace bluetooth
{

    class AVDTP
    {
      public:
        struct SbcConfiguration
        {
            int reconfigure;

            int numChannels;
            int samplingFrequency;
            int blockLength;


@@ 29,12 29,15 @@ namespace bluetooth
            btstack_sbc_channel_mode_t channelMode;
            btstack_sbc_allocation_method_t allocationMethod;
        };

        static void dumpSbcConfiguration();

        static constexpr int defaultSampleRate          = 44100;
        static constexpr auto sbcCodecConfigurationSize = 4;

        static SbcConfiguration sbcConfig;
        static std::array<std::uint8_t, sbcCodecConfigurationSize> sbcCodecConfiguration;
        static btstack_sbc_encoder_state_t sbcEncoderState;
        static std::array<uint8_t, 4> sbcCodecConfiguration;
        static constexpr int defaultSampleRate = 44100;
        static int sampleRate;

        static void dumpSbcConfiguration();
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp +74 -74
@@ 10,35 10,38 @@

namespace bluetooth
{
    std::uint8_t AVRCP::sdpTargetServiceBuffer[targetServiceBufferSize];
    std::uint8_t AVRCP::sdpControllerServiceBuffer[controllerServiceBufferSize];

    sys::Service *AVRCP::ownerService = nullptr;

    AVRCP::PlaybackStatusInfo AVRCP::playInfo;
    int AVRCP::currentTrackIndex;
    MediaContext AVRCP::mediaTracker;
    std::array<std::uint8_t, AVRCP::SDP_BUFFER_LENGTH> AVRCP::sdpTargetServiceBuffer;
    std::array<std::uint8_t, AVRCP::SDP_BUFFER_LENGTH> AVRCP::sdpControllerServiceBuffer;
    sys::Service *AVRCP::ownerService = nullptr;

    void AVRCP::packetHandler(uint8_t packetType,
                              [[maybe_unused]] uint16_t channel,
                              uint8_t *packet,
                              [[maybe_unused]] uint16_t size)
    void AVRCP::packetHandler(std::uint8_t packetType,
                              [[maybe_unused]] std::uint16_t channel,
                              std::uint8_t *packet,
                              [[maybe_unused]] std::uint16_t size)
    {
        bd_addr_t event_addr;
        std::uint16_t local_cid;
        std::uint8_t status = ERROR_CODE_SUCCESS;

        if (packetType != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        const auto subevent_code = hci_event_avrcp_meta_get_subevent_code(packet);

        switch (subevent_code) {
        case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED:
        case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED: {
            local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
            status    = avrcp_subevent_connection_established_get_status(packet);
            const auto status = avrcp_subevent_connection_established_get_status(packet);

            if (status != ERROR_CODE_SUCCESS) {
                LOG_INFO("AVRCP: Connection failed, local cid 0x%02x, status 0x%02x", local_cid, status);
                return;
                LOG_ERROR("Connection failed, local cid 0x%02X, status 0x%02X", local_cid, status);
                break;
            }

            AVRCP::mediaTracker.avrcp_cid = local_cid;
            avrcp_subevent_connection_established_get_bd_addr(packet, event_addr);



@@ 50,135 53,133 @@ namespace bluetooth
            avrcp_controller_enable_notification(AVRCP::mediaTracker.avrcp_cid,
                                                 AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED);

            avrcp_target_set_now_playing_info(
                AVRCP::mediaTracker.avrcp_cid, nullptr, sizeof(AVRCP::tracks) / sizeof(avrcp_track_t));
            avrcp_target_set_unit_info(AVRCP::mediaTracker.avrcp_cid, AVRCP_SUBUNIT_TYPE_AUDIO, AVRCP::companyId);
            avrcp_target_set_subunit_info(AVRCP::mediaTracker.avrcp_cid,
                                          AVRCP_SUBUNIT_TYPE_AUDIO,
                                          static_cast<const std::uint8_t *>(AVRCP::subunitInfo),
                                          sizeof(AVRCP::subunitInfo));

            avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP::playInfo.status);
            return;
        } break;

        case AVRCP_SUBEVENT_CONNECTION_RELEASED:
            LOG_INFO("AVRCP Target: Disconnected, avrcp_cid 0x%02x",
                     avrcp_subevent_connection_released_get_avrcp_cid(packet));
        case AVRCP_SUBEVENT_CONNECTION_RELEASED: {
            LOG_INFO("Disconnected, avrcp_cid 0x%02X", avrcp_subevent_connection_released_get_avrcp_cid(packet));
            AVRCP::mediaTracker.avrcp_cid = 0;
            return;
        } break;

        default:
            LOG_DEBUG("Event not handled: 0x%02X", subevent_code);
            break;
        }

        if (status != ERROR_CODE_SUCCESS) {
            LOG_INFO("Responding to event 0x%02x failed with status 0x%02x", subevent_code, status);
        }
    }

    void AVRCP::targetPacketHandler(uint8_t packetType,
                                    [[maybe_unused]] uint16_t channel,
                                    uint8_t *packet,
                                    [[maybe_unused]] uint16_t size)
    void AVRCP::targetPacketHandler(std::uint8_t packetType,
                                    [[maybe_unused]] std::uint16_t channel,
                                    std::uint8_t *packet,
                                    [[maybe_unused]] std::uint16_t size)
    {
        std::uint8_t status = ERROR_CODE_SUCCESS;

        if (packetType != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        const auto subevent_code = hci_event_avrcp_meta_get_subevent_code(packet);

        switch (subevent_code) {
        case AVRCP_SUBEVENT_PLAY_STATUS_QUERY:
            status = avrcp_target_play_status(AVRCP::mediaTracker.avrcp_cid,
                                              AVRCP::playInfo.song_length_ms,
                                              AVRCP::playInfo.song_position_ms,
                                              AVRCP::playInfo.status);
            break;
        case AVRCP_SUBEVENT_PLAY_STATUS_QUERY: {
            const auto status = avrcp_target_play_status(AVRCP::mediaTracker.avrcp_cid,
                                                         AVRCP::playInfo.song_length_ms,
                                                         AVRCP::playInfo.song_position_ms,
                                                         AVRCP::playInfo.status);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_ERROR("Play status query failed, status: 0x%02X", status);
            }
        } break;

        case AVRCP_SUBEVENT_OPERATION: {
            const auto operation_id =
                static_cast<avrcp_operation_id_t>(avrcp_subevent_operation_get_operation_id(packet));

            switch (operation_id) {
            case AVRCP_OPERATION_ID_PLAY: {
                LOG_INFO("AVRCP Target: PLAY");
                auto &busProxy = AVRCP::ownerService->bus;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::AudioStart>(), service::name::audio);
                return;
                LOG_INFO("AVRCP operation: PLAY");
                ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::AudioStart>(), service::name::audio);
            } break;

            case AVRCP_OPERATION_ID_PAUSE: {
                LOG_INFO("AVRCP Target: PAUSE");
                auto &busProxy = AVRCP::ownerService->bus;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::AudioPause>(), service::name::audio);
                return;
                LOG_INFO("AVRCP operation: PAUSE");
                ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::AudioPause>(), service::name::audio);
            } break;
            case AVRCP_OPERATION_ID_STOP:
                LOG_INFO("AVRCP Target: STOP");
                status = a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
                break;

            case AVRCP_OPERATION_ID_STOP: {
                LOG_INFO("AVRCP operation: STOP");
                const auto status = a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
                if (status != ERROR_CODE_SUCCESS) {
                    LOG_ERROR("Stop request failed, status: 0x%02X", status);
                }
            } break;

            default:
                LOG_INFO("AVRCP Target: operation 0x%2x is not handled", operation_id);
                return;
                LOG_INFO("Operation not handled: 0x%02X ", operation_id);
                break;
            }
            break;
        }

        default:
            LOG_DEBUG("Event not handled: 0x%02X", subevent_code);
            break;
        }

        if (status != ERROR_CODE_SUCCESS) {
            LOG_INFO("Responding to event 0x%02x failed with status 0x%02x", subevent_code, status);
        }
    }

    void AVRCP::controllerPacketHandler(uint8_t packetType,
                                        [[maybe_unused]] uint16_t channel,
                                        uint8_t *packet,
                                        [[maybe_unused]] uint16_t size)
    void AVRCP::controllerPacketHandler(std::uint8_t packetType,
                                        [[maybe_unused]] std::uint16_t channel,
                                        std::uint8_t *packet,
                                        [[maybe_unused]] std::uint16_t size)
    {
        if (packetType != HCI_EVENT_PACKET || hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        if (AVRCP::mediaTracker.avrcp_cid == 0u) {
        if (AVRCP::mediaTracker.avrcp_cid == 0) {
            return;
        }

        const auto subevent_code = hci_event_avrcp_meta_get_subevent_code(packet);

        switch (subevent_code) {
        /* BT device requested volume change */
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED: {
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED: { // BT device requested volume change
            const auto volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet);
            auto &busProxy    = AVRCP::ownerService->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::A2DPVolume>(volume), service::name::bluetooth);
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::A2DPVolume>(volume),
                                          service::name::bluetooth);
            LOG_INFO("AVRCP Controller: BT device volume changed to %u%% (%u)",
                     AVRCP::controllerVolumeToPercent(volume),
                     volume);
        } break;

        case AVRCP_SUBEVENT_NOTIFICATION_EVENT_BATT_STATUS_CHANGED:
            // see avrcp_battery_status_t
        case AVRCP_SUBEVENT_NOTIFICATION_EVENT_BATT_STATUS_CHANGED: { // see avrcp_battery_status_t
            LOG_INFO("AVRCP Controller: Notification Battery Status %d",
                     avrcp_subevent_notification_event_batt_status_changed_get_battery_status(packet));
            break;
        case AVRCP_SUBEVENT_NOTIFICATION_STATE:
        } break;

        case AVRCP_SUBEVENT_NOTIFICATION_STATE: {
            LOG_INFO("AVRCP Controller: Notification %s - %s",
                     avrcp_event2str(avrcp_subevent_notification_state_get_event_id(packet)),
                     avrcp_subevent_notification_state_get_enabled(packet) != 0 ? "enabled" : "disabled");
            break;
        } break;

        default:
            break;
        }
    }

    void AVRCP::init(sys::Service *service)
    {
        AVRCP::ownerService = service;
        // Initialize AVRCP Service.

        avrcp_init();
        avrcp_register_packet_handler(&packetHandler);
        // Initialize AVRCP Target.

        avrcp_target_init();
        avrcp_target_register_packet_handler(&targetPacketHandler);
        // Initialize AVRCP Controller

        avrcp_controller_init();
        avrcp_controller_register_packet_handler(&controllerPacketHandler);
    }


@@ 192,5 193,4 @@ namespace bluetooth
    {
        return volume * 100 / AVRCP::maxVolumeValue;
    }

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp +29 -17
@@ 1,8 1,10 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "A2DPImpl.hpp"

extern "C"
{
#include "classic/avrcp.h"


@@ 25,30 27,40 @@ namespace bluetooth
        static std::uint16_t controllerVolumeToPercent(std::uint16_t volume);

      public:
        static constexpr uint8_t subunitInfo[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
                                                  4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7};
        static constexpr uint32_t companyId    = 0x112233;
        static constexpr int SDP_BUFFER_LENGTH = 200;
        struct PlaybackStatusInfo
        {
            uint8_t track_id[8];
            uint32_t song_length_ms;
            std::uint8_t track_id[8];
            std::uint32_t song_length_ms;
            avrcp_playback_status_t status = AVRCP_PLAYBACK_STATUS_STOPPED;
            uint32_t song_position_ms; // 0xFFFFFFFF if not supported
            std::uint32_t song_position_ms; // 0xFFFFFFFF if not supported
        };

        static std::array<std::uint8_t, SDP_BUFFER_LENGTH> sdpTargetServiceBuffer;
        static std::array<std::uint8_t, SDP_BUFFER_LENGTH> sdpControllerServiceBuffer;
        static void packetHandler(std::uint8_t packetType,
                                  std::uint16_t channel,
                                  std::uint8_t *packet,
                                  std::uint16_t size);
        static void targetPacketHandler(std::uint8_t packetType,
                                        std::uint16_t channel,
                                        std::uint8_t *packet,
                                        std::uint16_t size);
        static void controllerPacketHandler(std::uint8_t packetType,
                                            std::uint16_t channel,
                                            std::uint8_t *packet,
                                            std::uint16_t size);
        static void init(sys::Service *service);

        static constexpr std::uint8_t subunitInfo[]       = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
                                                       4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7};
        static constexpr std::uint32_t companyId          = 0x112233;
        static constexpr auto targetServiceBufferSize     = 200;
        static constexpr auto controllerServiceBufferSize = 200;

        static std::uint8_t sdpTargetServiceBuffer[targetServiceBufferSize];
        static std::uint8_t sdpControllerServiceBuffer[controllerServiceBufferSize];

        static sys::Service *ownerService;

        static avrcp_track_t tracks[3];
        static int currentTrackIndex;
        static PlaybackStatusInfo playInfo;
        static MediaContext mediaTracker;

        static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void init(sys::Service *service);
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/BtCommand.cpp => module-bluetooth/Bluetooth/interface/profiles/BtCommand.cpp +3 -5
@@ 1,10 1,8 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BtCommand.hpp"

#include <array>
#include <cstdint>

extern "C"
{


@@ 15,13 13,13 @@ namespace bluetooth
{
    // Set local name with a template Bluetooth address, that will be automatically
    // replaced with a actual address once it is available, i.e. when BTstack boots
    Error set_name(std::string &name)
    Result set_name(std::string &name)
    {
        // name has to have storage
        constexpr std::uint32_t size = 64;
        static std::array<char, size> lname;
        snprintf(lname.data(), size, "%s", name.c_str());
        gap_set_local_name(lname.data());
        return Error();
        return Result();
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp +7 -7
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "GAP.hpp"


@@ 42,17 42,17 @@ namespace bluetooth
        return *dev;
    };

    auto GAP::registerScan() -> Error
    auto GAP::registerScan() -> Result
    {
        LOG_INFO("GAP register scan!");
        /// -> this have to be called prior to power on!
        hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR);
        cb_handler.callback = &packetHandler;
        hci_add_event_handler(&cb_handler);
        return Error();
        return Result();
    }

    auto GAP::scan() -> Error
    auto GAP::scan() -> Result
    {
        if (hci_get_state() == HCI_STATE_WORKING) {
            if (gap::state == gap::state::scan_on) {


@@ 61,14 61,14 @@ namespace bluetooth
            devices().clear();
            if (auto ret = startScan(); ret != 0) {
                LOG_ERROR("Start scan error!: 0x%02X - %s", ret, error_cstr(ret));
                return Error(Error::LibraryError, ret);
                return Result(Result::Code::LibraryError, ret);
            }
            gap::state = gap::state::scan_on;
        }
        else {
            return Error(Error::NotReady);
            return Result(Result::Code::NotReady);
        }
        return Error();
        return Result();
    }

    void GAP::stopScan()

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp +8 -6
@@ 1,15 1,18 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <vector>
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <Bluetooth/Result.hpp>
#include <Service/Service.hpp>

extern "C"
{
#include "btstack_defines.h"
}

namespace bluetooth
{
    namespace stack


@@ 47,12 50,11 @@ namespace bluetooth
        static void processDedicatedBondingCompleted(std::uint8_t *packet, bd_addr_t &addr);
        static void processSimplePairingCompleted(std::uint8_t *packet, bd_addr_t &addr);
        static void initStateHandler(std::uint8_t eventType, std::uint8_t *packet);
        static auto getDeviceIndexForAddress(const std::vector<Devicei> &devs, const bd_addr_t addr) -> int;

      public:
        /// THIS have to be called prior to Bt system start!
        static auto registerScan() -> Error;
        auto scan() -> Error;
        static auto registerScan() -> Result;
        auto scan() -> Result;
        void stopScan();
        void setVisibility(bool visibility);
        void pair(Devicei device, std::uint8_t protectionLevel = 0);


@@ 60,7 62,7 @@ namespace bluetooth
        static auto getDevicesList() -> std::vector<Devicei>;
        static void respondPinCode(const std::string &pin, Devicei d);
        static void finishCodeComparison(bool accepted, Devicei d);
        static Devicei currentlyProccesedDevice;
        static Devicei currentlyProcessedDevice;
        explicit GAP(sys::Service *owner);
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp +253 -226
@@ 4,7 4,7 @@
#include "HFPImpl.hpp"
#include "HFP.hpp"

#include <Bluetooth/Error.hpp>
#include <Bluetooth/Result.hpp>
#include <log/log.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-audio/AudioMessage.hpp>


@@ 16,11 16,12 @@
#include <BluetoothWorker.hpp>
#include "SCO/ScoUtils.hpp"

#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))

extern "C"
{
#include "btstack.h"
#include "btstack_run_loop_freertos.h"
#include "btstack_stdin.h"
#include <btstack_defines.h>
}



@@ 41,7 42,7 @@ namespace bluetooth
        return *this;
    }

    auto HFP::init() -> Error::Code
    auto HFP::init() -> Result::Code
    {
        return pimpl->init();
    }


@@ 51,9 52,8 @@ namespace bluetooth
        pimpl->setDevice(device);
    }

    void HFP::setOwnerService(const sys::Service *service)
    void HFP::setOwnerService(sys::Service *service)
    {
        ownerService = service;
        pimpl->setOwnerService(service);
    }



@@ 66,68 66,71 @@ namespace bluetooth
    {
        pimpl->disconnect();
    }
    auto HFP::startRinging() const noexcept -> Error::Code

    auto HFP::incomingCallStarted() const noexcept -> Result::Code
    {
        pimpl->startRinging();
        return Error::Success;
        return pimpl->incomingCallStarted();
    }
    auto HFP::stopRinging() const noexcept -> Error::Code

    auto HFP::outgoingCallStarted(const std::string &number) const noexcept -> Result::Code
    {
        pimpl->stopRinging();
        return Error::Success;
        return pimpl->outgoingCallStarted(number);
    }
    auto HFP::initializeCall() const noexcept -> Error::Code

    auto HFP::incomingCallAnswered() const noexcept -> Result::Code
    {
        pimpl->initializeCall();
        return Error::Success;
        return pimpl->incomingCallAnswered();
    }
    auto HFP::terminateCall() const noexcept -> Error::Code

    auto HFP::outgoingCallAnswered() const noexcept -> Result::Code
    {
        pimpl->terminateCall();
        return Error::Success;
        return pimpl->outgoingCallAnswered();
    }
    void HFP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)

    auto HFP::setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code
    {
        pimpl->setAudioDevice(audioDevice);
        return pimpl->setIncomingCallNumber(num);
    }
    auto HFP::callActive() const noexcept -> Error::Code

    auto HFP::callTerminated() const noexcept -> Result::Code
    {
        return pimpl->callActive();
        return pimpl->callTerminated();
    }
    auto HFP::setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code

    auto HFP::callMissed() const noexcept -> Result::Code
    {
        LOG_SENSITIVE(LOGDEBUG, "Setting number: %s", num.c_str());
        return pimpl->setIncomingCallNumber(num);
        return pimpl->callMissed();
    }
    auto HFP::setSignalStrength(int bars) const noexcept -> Error::Code

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

    auto HFP::setSignalStrength(int bars) const noexcept -> Result::Code
    {
        LOG_DEBUG("Setting RSSI bars: %d/4", bars);
        return pimpl->setSignalStrength(bars);
    }
    auto HFP::setOperatorName(const std::string_view &name) const noexcept -> Error::Code

    auto HFP::setOperatorName(const std::string_view &name) const noexcept -> Result::Code
    {
        LOG_DEBUG("Setting operator name: %s", name.data());
        return pimpl->setOperatorName(name);
    }
    auto HFP::setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code

    auto HFP::setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code
    {
        LOG_DEBUG("Setting battery level: %d", level.getBatteryLevel());
        return pimpl->setBatteryLevel(level);
    }
    auto HFP::setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code

    auto HFP::setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code
    {
        LOG_DEBUG("Setting network registration status: %s", registered ? "online" : "offline");
        return pimpl->setNetworkRegistrationStatus(registered);
    }
    auto HFP::setRoamingStatus(bool enabled) const noexcept -> Error::Code

    auto HFP::setRoamingStatus(bool enabled) const noexcept -> Result::Code
    {
        LOG_DEBUG("Setting roaming status: %s", enabled ? "enabled" : "disabled");
        return pimpl->setRoamingStatus(enabled);
    }
    auto HFP::callStarted(const std::string &num) const noexcept -> Error::Code
    {
        return pimpl->callStarted(num);
    }

    HFP::~HFP()
    {


@@ 137,19 140,23 @@ namespace bluetooth

    hci_con_handle_t HFP::HFPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
    hci_con_handle_t HFP::HFPImpl::aclHandle = HCI_CON_HANDLE_INVALID;
    std::array<uint8_t, serviceBufferLength> HFP::HFPImpl::serviceBuffer;

    std::uint8_t HFP::HFPImpl::serviceBuffer[serviceBufferSize];

    std::unique_ptr<SCO> HFP::HFPImpl::sco;
    std::unique_ptr<CellularInterface> HFP::HFPImpl::cellularInterface = nullptr;
    std::unique_ptr<AudioInterface> HFP::HFPImpl::audioInterface       = nullptr;
    const sys::Service *HFP::HFPImpl::ownerService;
    const std::string_view HFP::HFPImpl::agServiceName = "Mudita Pure HFP";
    SCOCodec HFP::HFPImpl::codec                       = SCOCodec::CVSD;
    std::shared_ptr<CVSDAudioDevice> HFP::HFPImpl::audioDevice;

    int HFP::HFPImpl::memory_1_enabled = 1;
    btstack_packet_callback_registration_t HFP::HFPImpl::hci_event_callback_registration;
    int HFP::HFPImpl::ag_indicators_nr               = 7;
    hfp_ag_indicator_t HFP::HFPImpl::ag_indicators[] = {
    std::unique_ptr<CellularInterface> HFP::HFPImpl::cellularInterface;
    std::unique_ptr<AudioInterface> HFP::HFPImpl::audioInterface;

    sys::Service *HFP::HFPImpl::ownerService;
    Devicei HFP::HFPImpl::device;

    const char *HFP::HFPImpl::agServiceName = "Mudita Pure HFP";
    btstack_packet_callback_registration_t HFP::HFPImpl::hciEventCallbackRegistration;

    hfp_ag_indicator_t HFP::HFPImpl::agIndicators[] = {
        // index, name, min range, max range, status, mandatory, enabled, status changed
        {1, "service", 0, 1, 1, 0, 0, 0},
        {2, "call", 0, 1, 0, 1, 1, 0},


@@ 158,17 165,15 @@ namespace bluetooth
        {5, "signal", 0, 4, 5, 0, 1, 0},
        {6, "roam", 0, 1, 0, 0, 1, 0},
        {7, "callheld", 0, 2, 0, 1, 1, 0}};
    int HFP::HFPImpl::call_hold_services_nr                      = 5;
    const char *HFP::HFPImpl::call_hold_services[]               = {"1", "1x", "2", "2x", "3"};
    int HFP::HFPImpl::hf_indicators_nr                           = 2;
    hfp_generic_status_indicator_t HFP::HFPImpl::hf_indicators[] = {
        {1, 1},
        {2, 1},

    const char *HFP::HFPImpl::callHoldServices[] = {"1", "1x", "2", "2x", "3"};

    hfp_generic_status_indicator_t HFP::HFPImpl::hfIndicators[] = {
        {1, 1}, // Enhanced safety indicator
        {2, 1}, // Remaining level of battery indicator
    };
    Devicei HFP::HFPImpl::device;
    CallStatus HFP::HFPImpl::currentCallStatus = CallStatus::Unknown;

    void HFP::HFPImpl::dump_supported_codecs(void)
    void HFP::HFPImpl::logSupportedCodecs()
    {
        LOG_DEBUG("Supported codecs: ");



@@ 184,7 189,7 @@ namespace bluetooth
                LOG_DEBUG("mSBC");
            }
            else {
                LOG_WARN("mSBC codec disabled because eSCO not supported by local controller.");
                LOG_WARN("mSBC codec disabled because eSCO not supported by local controller");
            }
            break;



@@ 193,15 198,18 @@ namespace bluetooth
            break;
        }
    }

    void HFP::HFPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {
        auto evt       = std::make_shared<audio::Event>(event, state);
        auto msg       = std::make_shared<AudioEventRequest>(std::move(evt));
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
        ownerService->bus.sendUnicast(std::move(msg), service::name::evt_manager);
    }

    void HFP::HFPImpl::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize)
    void HFP::HFPImpl::packetHandler(std::uint8_t packetType,
                                     [[maybe_unused]] std::uint16_t channel,
                                     std::uint8_t *event,
                                     std::uint16_t eventSize)
    {
        switch (packetType) {
        case HCI_SCO_DATA_PACKET:


@@ 212,7 220,6 @@ namespace bluetooth
                audioDevice->receiveCVSD(audio::AbstractStream::Span{.data = event, .dataSize = eventSize});
            }
            break;

        case HCI_EVENT_PACKET:
            processHCIEvent(event);
            break;


@@ 221,7 228,7 @@ namespace bluetooth
        }
    }

    void HFP::HFPImpl::processHCIEvent(uint8_t *event)
    void HFP::HFPImpl::processHCIEvent(std::uint8_t *event)
    {
        switch (hci_event_packet_get_type(event)) {
        case HCI_EVENT_SCO_CAN_SEND_NOW:


@@ 240,121 247,122 @@ namespace bluetooth
        }
    }

    void HFP::HFPImpl::processHFPEvent(uint8_t *event)
    void HFP::HFPImpl::processHFPEvent(std::uint8_t *event)
    {
        switch (hci_event_hfp_meta_get_subevent_code(event)) {
        case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
            std::uint8_t status;
            status = hfp_subevent_service_level_connection_established_get_status(event);
            if (status) {
                LOG_DEBUG("Connection failed, status 0x%02x", status);
        case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED: {
            const auto status = hfp_subevent_service_level_connection_established_get_status(event);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_ERROR("Connection failed, status: 0x%02X", status);
                sendAudioEvent(audio::EventType::BluetoothHFPDeviceState, audio::Event::DeviceState::Disconnected);
                break;
            }

            aclHandle = hfp_subevent_service_level_connection_established_get_acl_handle(event);
            hfp_subevent_service_level_connection_established_get_bd_addr(event, device.address);
            LOG_DEBUG("Service level connection established to %s", bd_addr_to_str(device.address));
            LOG_INFO("Service level connection established to %s", bd_addr_to_str(device.address));

            sendAudioEvent(audio::EventType::BluetoothHFPDeviceState, audio::Event::DeviceState::Connected);
            {
                auto &busProxy     = const_cast<sys::Service *>(ownerService)->bus;
                device.deviceState = DeviceState::ConnectedVoice;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, true),
                                     service::name::bluetooth);
                // request cellular status indicators for HFP
                busProxy.sendUnicast(std::make_shared<message::bluetooth::RequestStatusIndicatorData>(),
                                     service::name::bluetooth);
            }
            dump_supported_codecs();
            break;
        case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
            LOG_DEBUG("Service level connection released");
            device.deviceState = DeviceState::ConnectedVoice;

            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(
                                              device, message::bluetooth::ConnectResult::Result::Success),
                                          service::name::bluetooth);
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::RequestStatusIndicatorData>(),
                                          service::name::bluetooth);

            logSupportedCodecs();
        } break;

        case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED: {
            aclHandle = HCI_CON_HANDLE_INVALID;

            sendAudioEvent(audio::EventType::BluetoothHFPDeviceState, audio::Event::DeviceState::Disconnected);
            {
                auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                     service::name::bluetooth);
            }
            break;
        case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
            if (hfp_subevent_audio_connection_established_get_status(event)) {
                LOG_DEBUG("Audio connection establishment failed with status %u",
                          hfp_subevent_audio_connection_established_get_status(event));
            }
            else {
                scoHandle = hfp_subevent_audio_connection_established_get_sco_handle(event);
                LOG_DEBUG("Audio connection established with SCO handle 0x%04x", scoHandle);
                codec = static_cast<SCOCodec>(hfp_subevent_audio_connection_established_get_negotiated_codec(event));
                dump_supported_codecs();
                audioInterface->startAudioRouting(const_cast<sys::Service *>(ownerService));
                hci_request_sco_can_send_now_event();
                RunLoop::trigger();
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                          service::name::bluetooth);

            LOG_INFO("Service level connection released");
        } break;

        case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED: {
            const auto status = hfp_subevent_audio_connection_established_get_status(event);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_ERROR("Audio connection establishment failed, status: 0x%02X", status);
                break;
            }
            break;
        case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
            LOG_DEBUG("Audio connection released");

            scoHandle = hfp_subevent_audio_connection_established_get_sco_handle(event);
            LOG_INFO("Audio connection established with SCO handle 0x%04X", scoHandle);

            codec = static_cast<SCOCodec>(hfp_subevent_audio_connection_established_get_negotiated_codec(event));
            logSupportedCodecs();

            audioInterface->startAudioRouting(ownerService);
            hci_request_sco_can_send_now_event();
            RunLoop::trigger();
        } break;

        case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED: {
            scoHandle = HCI_CON_HANDLE_INVALID;
            audioDevice.reset();
            break;
            LOG_INFO("Audio connection released");
        } break;

        case HFP_SUBEVENT_START_RINGING:
            LOG_DEBUG("Start Ringing");
            // todo request here ringtone stream
            break;

        case HFP_SUBEVENT_STOP_RINGING:
            LOG_DEBUG("Stop Ringing");
            // todo stop ringtone stream here
            break;
        case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER: {
            auto receivedNumber = hfp_subevent_place_call_with_number_get_number(event);
            LOG_DEBUG("Requested call from HFP to number %s", receivedNumber);
            currentCallStatus = CallStatus::OutgoingPlacedFromHFP;
            hfp_ag_outgoing_call_accepted();
            cellularInterface->dialNumber(const_cast<sys::Service *>(ownerService), receivedNumber);
        } break;

        case HFP_SUBEVENT_RING:
            LOG_DEBUG("SUBEVENT_RING called!");
            break;

        case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER: {
            const auto receivedNumber = hfp_subevent_place_call_with_number_get_number(event);
            LOG_INFO("Requested call from HFP to number %s", receivedNumber);
            cellularInterface->dialNumber(ownerService, receivedNumber);
        } break;

        case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG:
            // todo has to be feeded with proper phone number from cellular
            // LOG_DEBUG("Attach number to voice tag. Sending '1234567'");
            hfp_ag_send_phone_number_for_voice_tag(aclHandle, "1234567");
            break;

        case HFP_SUBEVENT_TRANSMIT_DTMF_CODES: {
            auto digitStr = hfp_subevent_transmit_dtmf_codes_get_dtmf(event);
            const auto digitStr = hfp_subevent_transmit_dtmf_codes_get_dtmf(event);
            LOG_DEBUG("Send DTMF Codes: '%s'", digitStr);
            try {
                cellularInterface->sendDTMFCode(const_cast<sys::Service *>(ownerService), DTMFCode(digitStr));
                cellularInterface->sendDTMFCode(ownerService, DTMFCode(digitStr));
            }
            catch (std::out_of_range &e) {
                LOG_ERROR("Can't send DTMF code for digit: %s", digitStr);
            }
            hfp_ag_send_dtmf_code_done(aclHandle);
        } break;
        case HFP_SUBEVENT_CALL_ANSWERED:
            LOG_DEBUG("Call answered");
            cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
            break;

        case HFP_SUBEVENT_CALL_ANSWERED: {
            LOG_INFO("Event: call answered");
            cellularInterface->answerIncomingCall(ownerService);
        } break;

        case HFP_SUBEVENT_SPEAKER_VOLUME: {
            const auto volume = hfp_subevent_speaker_volume_get_gain(event);
            auto &busProxy    = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::HFPVolume>(volume), service::name::bluetooth);
            LOG_DEBUG("Received speaker gain change %d", hsp_subevent_speaker_gain_changed_get_gain(event));
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::HFPVolume>(volume),
                                          service::name::bluetooth);
            LOG_INFO("Received speaker gain change to %d", volume);
        } break;

        case HFP_SUBEVENT_CALL_TERMINATED: {
            LOG_INFO("Event: call terminated");
            cellularInterface->hangupCall(ownerService);
        } break;

        case HFP_SUBEVENT_CALL_TERMINATED:
            LOG_DEBUG("Call terminated");
            cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
            currentCallStatus = CallStatus::Unknown;
            break;
        default:
            LOG_DEBUG("Event not handled %u", hci_event_hfp_meta_get_subevent_code(event));
            LOG_INFO("Event not handled: 0x%02X", hci_event_hfp_meta_get_subevent_code(event));
            break;
        }
    }

    static hfp_phone_number_t subscriber_number = {129, "225577"};
    auto HFP::HFPImpl::init() -> Error::Code
    auto HFP::HFPImpl::init() -> Result::Code
    {
        sco = std::make_unique<SCO>();
        sco->setOwnerService(ownerService);


@@ 366,51 374,58 @@ namespace bluetooth
        Profile::initL2cap();
        Profile::initSdp();

        serviceBuffer.fill(0);
        uint16_t supported_features = (1 << HFP_AGSF_ESCO_S4) | /* (1 << HFP_AGSF_HF_INDICATORS) | */
                                      (1 << HFP_AGSF_CODEC_NEGOTIATION) | (1 << HFP_AGSF_EXTENDED_ERROR_RESULT_CODES) |
                                      (1 << HFP_AGSF_ENHANCED_CALL_CONTROL) | (1 << HFP_AGSF_ENHANCED_CALL_STATUS) |
                                      (1 << HFP_AGSF_ABILITY_TO_REJECT_A_CALL) /*| (1 << HFP_AGSF_IN_BAND_RING_TONE) |*/
            /* (1 << HFP_AGSF_VOICE_RECOGNITION_FUNCTION) |(1 << HFP_AGSF_THREE_WAY_CALLING)*/;
        int wide_band_speech                       = 0;
        constexpr std::uint8_t abilityToRejectCall = 1;
        hfp_ag_create_sdp_record(serviceBuffer.data(),
        std::memset(serviceBuffer, 0, sizeof(serviceBuffer));

        constexpr auto wideBandSpeechEnabled = 0;
        constexpr auto abilityToRejectCall   = 1;
        constexpr std::uint16_t supportedFeatures =
            (1 << HFP_AGSF_ESCO_S4) | (1 << HFP_AGSF_CODEC_NEGOTIATION) | (1 << HFP_AGSF_EXTENDED_ERROR_RESULT_CODES) |
            (1 << HFP_AGSF_ENHANCED_CALL_CONTROL) | (1 << HFP_AGSF_ENHANCED_CALL_STATUS) |
            (1 << HFP_AGSF_ABILITY_TO_REJECT_A_CALL) | (1 << HFP_AGSF_ATTACH_A_NUMBER_TO_A_VOICE_TAG);

        hfp_ag_create_sdp_record(serviceBuffer,
                                 hfpSdpRecordHandle,
                                 rfcommChannelNr,
                                 agServiceName.data(),
                                 agServiceName,
                                 abilityToRejectCall,
                                 supported_features,
                                 wide_band_speech);

        if (const auto status = sdp_register_service(serviceBuffer.data()); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service. Status %x", status);
                                 supportedFeatures,
                                 wideBandSpeechEnabled);
        if (const auto status = sdp_register_service(serviceBuffer); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service, status 0x%02X", status);
        }

        rfcomm_init();

        hfp_ag_init(rfcommChannelNr);
        hfp_ag_init_supported_features(supported_features);
        hfp_ag_init_supported_features(supportedFeatures);

        initCodecs();
        hfp_ag_init_ag_indicators(ag_indicators_nr, ag_indicators);
        hfp_ag_init_hf_indicators(hf_indicators_nr, hf_indicators);
        hfp_ag_init_call_hold_services(call_hold_services_nr, call_hold_services);
        hfp_ag_set_subcriber_number_information(&subscriber_number, 1);

        hci_event_callback_registration.callback = &packetHandler;
        hci_add_event_handler(&hci_event_callback_registration);
        hci_register_sco_packet_handler(&packetHandler);
        hfp_ag_init_ag_indicators(ARRAY_LENGTH(agIndicators), agIndicators);
        hfp_ag_init_hf_indicators(ARRAY_LENGTH(hfIndicators), hfIndicators);

        hfp_ag_init_call_hold_services(ARRAY_LENGTH(callHoldServices), callHoldServices);

        /* TODO here own number from cellular should be provided */
        hfp_ag_set_subcriber_number_information(&subscriber_number, 0);

        hciEventCallbackRegistration.callback = &packetHandler;
        hci_add_event_handler(&hciEventCallbackRegistration);
        hci_register_sco_packet_handler(&packetHandler);
        hfp_ag_register_packet_handler(&packetHandler);

        hfp_ag_set_use_in_band_ring_tone(0);

        LOG_INFO("HFP init done!");

        return bluetooth::Error::Success;
        LOG_INFO("HFP initialized!");
        return bluetooth::Result::Code::Success;
    }

    void HFP::HFPImpl::connect()
    {
        /* Disconnect before attempting to connect */
        disconnect();
        LOG_DEBUG("Connecting the HFP profile");

        LOG_INFO("Connecting HFP profile");
        hfp_ag_establish_service_level_connection(device.address);
        hfp_ag_set_speaker_gain(aclHandle, 8);
        hfp_ag_set_microphone_gain(aclHandle, 10);


@@ 418,20 433,20 @@ namespace bluetooth

    void HFP::HFPImpl::disconnect()
    {
        LOG_INFO("Disconnecting HFP profile");
        if (hfp_ag_release_service_level_connection(aclHandle) == ERROR_CODE_SUCCESS) {
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                 service::name::bluetooth);
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                          service::name::bluetooth);
        }
    }

    void HFP::HFPImpl::setDevice(Devicei dev)
    {
        device = dev;
        device = std::move(dev);
        LOG_DEBUG("Device set!");
    }

    void HFP::HFPImpl::setOwnerService(const sys::Service *service)
    void HFP::HFPImpl::setOwnerService(sys::Service *service)
    {
        ownerService = service;
    }


@@ 453,99 468,111 @@ namespace bluetooth
            codecsList.push_back(SCOCodec::mSBC);
            break;
        }
        hfp_ag_init_codecs(codecsList.size(), reinterpret_cast<uint8_t *>(codecsList.data()));
        hfp_ag_init_codecs(codecsList.size(), reinterpret_cast<std::uint8_t *>(codecsList.data()));
    }
    void HFP::HFPImpl::initializeCall() const noexcept
    {}
    void HFP::HFPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)

    void HFP::HFPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDev)
    {
        HFP::HFPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDevice);
        HFP::HFPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDev);
        HFP::HFPImpl::audioDevice->setAclHandle(aclHandle);
        LOG_DEBUG("Audiodevice set!");
    }
    void HFP::HFPImpl::startRinging() const noexcept

    auto HFP::HFPImpl::incomingCallStarted() const noexcept -> Result::Code
    {
        LOG_DEBUG("Starting incoming call");
        currentCallStatus = CallStatus::Incoming;
        LOG_DEBUG("Incoming call received");
        hfp_ag_incoming_call();
        return Result::Code::Success;
    }
    void HFP::HFPImpl::stopRinging() const noexcept

    auto HFP::HFPImpl::outgoingCallStarted(const std::string &number) const noexcept -> Result::Code
    {
        LOG_DEBUG("Stop ringing called!");
        LOG_DEBUG("Outgoing call placed");
        hfp_ag_outgoing_call_initiated();
        hfp_ag_outgoing_call_accepted();
        hfp_ag_outgoing_call_ringing();
        hfp_ag_send_phone_number_for_voice_tag(aclHandle, number.c_str());
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::callActive() const noexcept -> Error::Code

    auto HFP::HFPImpl::incomingCallAnswered() const noexcept -> Result::Code
    {
        if (currentCallStatus == CallStatus::Incoming) {
            hfp_ag_answer_incoming_call(); // will answer the call if it wasn't answered
        }
        else {
            hfp_ag_outgoing_call_established();
        }
        currentCallStatus = CallStatus::Active;
        return Error::Success;
        LOG_DEBUG("Incoming call answered");
        hfp_ag_answer_incoming_call();
        return Result::Code::Success;
    }
    void HFP::HFPImpl::terminateCall() const noexcept

    auto HFP::HFPImpl::outgoingCallAnswered() const noexcept -> Result::Code
    {
        if (currentCallStatus == CallStatus::Active) {
            hfp_ag_terminate_call();
        }
        else {
            hfp_ag_call_dropped();
        }
        LOG_DEBUG("Outgoing call answered");
        hfp_ag_outgoing_call_established();
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code

    auto HFP::HFPImpl::callTerminated() const noexcept -> Result::Code
    {
        hfp_ag_set_clip(129, num.c_str());
        return Error::Success;
        LOG_DEBUG("Outgoing call terminated");
        hfp_ag_terminate_call();
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::setSignalStrength(int bars) const noexcept -> Error::Code

    auto HFP::HFPImpl::callMissed() const noexcept -> Result::Code
    {
        hfp_ag_set_signal_strength(bars);
        return Error::Success;
        LOG_DEBUG("Incoming call missed");
        hfp_ag_terminate_call();
        return Result::Code::Success;
    }

    auto HFP::HFPImpl::setOperatorName(const std::string_view &name) const noexcept -> Error::Code
    auto HFP::HFPImpl::setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code
    {
        auto result = hfp_ag_set_operator_name(HFP::HFPImpl::aclHandle, name.data());
        LOG_DEBUG("Operator set name result: %d", result);
        return Error::Success;
        LOG_SENSITIVE(LOGDEBUG, "Setting number: %s", num.c_str());
        hfp_ag_set_clip(129, num.c_str());
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code

    auto HFP::HFPImpl::setSignalStrength(int bars) const noexcept -> Result::Code
    {
        auto result = hfp_ag_set_battery_level(level.getBatteryLevelBars());
        const auto result = hfp_ag_set_signal_strength(bars);
        LOG_INFO("Set RSSI bars: %d/4, result: 0x%02X", bars, result);
        return Result::Code::Success;
    }

        LOG_DEBUG("Battery level (bars): %d, set result: %d", level.getBatteryLevelBars(), result);
        return Error::Success;
    auto HFP::HFPImpl::setOperatorName(const std::string_view &name) const noexcept -> Result::Code
    {
        const auto result = hfp_ag_set_operator_name(aclHandle, name.data());
        LOG_INFO("Set operator name: %s, result: 0x%02X", name.data(), result);
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::callStarted(const std::string &number) const noexcept -> Error::Code

    auto HFP::HFPImpl::setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code
    {
        if (currentCallStatus != CallStatus::OutgoingPlacedFromHFP) {
            LOG_DEBUG("Started outgoing call from Pure");
            hfp_ag_outgoing_call_initiated();
            hfp_ag_outgoing_call_accepted();
            hfp_ag_outgoing_call_ringing();
            currentCallStatus = CallStatus::OutgoingPlacedFromPure;
        }
        return Error::Success;
        const auto batteryLevel = level.getBatteryLevelBars();
        const auto result       = hfp_ag_set_battery_level(static_cast<int>(batteryLevel));
        LOG_INFO("Set battery level: %d, result: 0x%02X", batteryLevel, result);
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code

    auto HFP::HFPImpl::setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code
    {
        hfp_ag_set_registration_status(registered);
        return Error::Success;
        const auto result = hfp_ag_set_registration_status(static_cast<int>(registered));
        LOG_INFO("Set registration status: %s, result 0x%02X", registered ? "online" : "offline", result);
        return Result::Code::Success;
    }
    auto HFP::HFPImpl::setRoamingStatus(bool enabled) const noexcept -> Error::Code

    auto HFP::HFPImpl::setRoamingStatus(bool enabled) const noexcept -> Result::Code
    {
        hfp_ag_set_roaming_status(enabled);
        return Error::Success;
        const auto result = hfp_ag_set_roaming_status(static_cast<int>(enabled));
        LOG_INFO("Set roaming status: %s, result: 0x%02X", enabled ? "enabled" : "disabled", result);
        return Result::Code::Success;
    }

    void HFP::HFPImpl::deInit() noexcept
    {
        LOG_DEBUG("[HFP] deinit");
        sdp_unregister_service(hfpSdpRecordHandle);

        cellularInterface.reset();
        audioInterface.reset();
        sco.reset();
    }

        LOG_INFO("HFP deinitialized!");
    }
} // namespace bluetooth

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

#pragma once


@@ 25,41 25,32 @@ namespace bluetooth
        HFP(HFP &&other) noexcept;
        auto operator=(HFP &&other) noexcept -> HFP &;

        auto init() -> Error::Code override;
        auto init() -> Result::Code override;
        void setDevice(const Devicei &device) override;
        void setOwnerService(const sys::Service *service) override;
        void setOwnerService(sys::Service *service) override;

        void connect() override;
        void disconnect() 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;
        [[nodiscard]] auto terminateCall() const noexcept -> Error::Code override;
        [[nodiscard]] auto callActive() const noexcept -> Error::Code override;
        [[nodiscard]] auto callStarted(const std::string &number) const noexcept -> Error::Code override;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code override;
        [[nodiscard]] auto incomingCallStarted() const noexcept -> Result::Code override;
        [[nodiscard]] auto outgoingCallStarted(const std::string &number) const noexcept -> Result::Code override;
        [[nodiscard]] auto incomingCallAnswered() const noexcept -> Result::Code override;
        [[nodiscard]] auto outgoingCallAnswered() const noexcept -> Result::Code override;
        [[nodiscard]] auto callTerminated() const noexcept -> Result::Code override;
        [[nodiscard]] auto callMissed() const noexcept -> Result::Code override;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code override;
        /// @brief Sets the signal strength bars data
        /// @return Success
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Error::Code override;
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Error::Code override;
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code override;
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code override;
        auto setRoamingStatus(bool enabled) const noexcept -> Error::Code override;
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Result::Code override;
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Result::Code override;
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code override;
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code override;
        auto setRoamingStatus(bool enabled) const noexcept -> Result::Code override;

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

      private:
        class HFPImpl;
        std::unique_ptr<HFPImpl> pimpl;
        const sys::Service *ownerService{};
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp +44 -48
@@ 1,77 1,73 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "HFP.hpp"
#include "Error.hpp"
#include "Result.hpp"
#include <interface/profiles/SCO/SCO.hpp>
#include <Audio/AudioCommon.hpp>

namespace bluetooth
{
    static constexpr int serviceBufferLength = 150;
    static constexpr int commandBufferLength = 150;

    enum class CallStatus
    {
        OutgoingPlacedFromPure,
        OutgoingPlacedFromHFP,
        Incoming,
        Active,
        Unknown
    };

    class HFP::HFPImpl
    {
      public:
        static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize);
        auto init() -> Error::Code;
        static void packetHandler(std::uint8_t packetType,
                                  std::uint16_t channel,
                                  std::uint8_t *event,
                                  std::uint16_t eventSize);
        auto init() -> Result::Code;
        void deInit() noexcept;
        void startRinging() const noexcept;
        void stopRinging() const noexcept;
        void initializeCall() const noexcept;
        void terminateCall() const noexcept;
        [[nodiscard]] auto incomingCallStarted() const noexcept -> Result::Code;
        [[nodiscard]] auto outgoingCallStarted(const std::string &number) const noexcept -> Result::Code;
        [[nodiscard]] auto incomingCallAnswered() const noexcept -> Result::Code;
        [[nodiscard]] auto outgoingCallAnswered() const noexcept -> Result::Code;
        [[nodiscard]] auto callTerminated() const noexcept -> Result::Code;
        [[nodiscard]] auto callMissed() const noexcept -> Result::Code;
        void connect();
        void disconnect();
        void setDevice(Devicei device);
        void setOwnerService(const sys::Service *service);
        void setOwnerService(sys::Service *service);
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);
        [[nodiscard]] auto callActive() const noexcept -> Error::Code;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code;
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Error::Code;
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Error::Code;
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code;
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code;
        [[nodiscard]] auto setRoamingStatus(bool enabled) const noexcept -> Error::Code;
        [[nodiscard]] auto callStarted(const std::string &number) const noexcept -> Error::Code;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code;
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Result::Code;
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Result::Code;
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code;
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code;
        [[nodiscard]] auto setRoamingStatus(bool enabled) const noexcept -> Result::Code;

      private:
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static void processHCIEvent(uint8_t *event);
        static void processHFPEvent(uint8_t *event);
        static void processHCIEvent(std::uint8_t *event);
        static void processHFPEvent(std::uint8_t *event);
        static void initCodecs();
        static void dump_supported_codecs(void);
        static std::array<uint8_t, serviceBufferLength> serviceBuffer;
        static constexpr uint8_t rfcommChannelNr = 1;
        static const std::string_view agServiceName;
        static void logSupportedCodecs();

        static constexpr auto serviceBufferSize           = 150;
        static constexpr std::uint8_t rfcommChannelNr     = 1;
        static constexpr std::uint32_t hfpSdpRecordHandle = 0x10004;

        static hci_con_handle_t scoHandle;
        static hci_con_handle_t aclHandle;

        static std::uint8_t serviceBuffer[serviceBufferSize];

        static const char *agServiceName;

        static std::unique_ptr<SCO> sco;
        static std::unique_ptr<CellularInterface> cellularInterface;
        static std::unique_ptr<AudioInterface> audioInterface;
        static const sys::Service *ownerService;
        static SCOCodec codec;
        [[maybe_unused]] static int memory_1_enabled;
        static btstack_packet_callback_registration_t hci_event_callback_registration;
        [[maybe_unused]] static int ag_indicators_nr;
        static hfp_ag_indicator_t ag_indicators[7];
        [[maybe_unused]] static int call_hold_services_nr;
        static const char *call_hold_services[5];
        [[maybe_unused]] static int hf_indicators_nr;
        [[maybe_unused]] static hfp_generic_status_indicator_t hf_indicators[2];
        static constexpr std::uint32_t hfpSdpRecordHandle = 0x10004;
        static std::shared_ptr<CVSDAudioDevice> audioDevice;

        static std::unique_ptr<CellularInterface> cellularInterface;
        static std::unique_ptr<AudioInterface> audioInterface;

        static sys::Service *ownerService;
        static Devicei device;
        static CallStatus currentCallStatus;

        static btstack_packet_callback_registration_t hciEventCallbackRegistration;
        static hfp_ag_indicator_t agIndicators[];
        static const char *callHoldServices[];
        static hfp_generic_status_indicator_t hfIndicators[];
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +204 -180
@@ 5,7 5,7 @@
#include "HSPImpl.hpp"
#include "HSP.hpp"

#include <Bluetooth/Error.hpp>
#include <Bluetooth/Result.hpp>
#include <service-evtmgr/Constants.hpp>
#include <BluetoothWorker.hpp>
#include <module-bluetooth/Bluetooth/interface/profiles/SCO/ScoUtils.hpp>


@@ 14,15 14,11 @@
#include <service-bluetooth/messages/AudioVolume.hpp>
#include <service-bluetooth/messages/Connect.hpp>
#include <service-bluetooth/messages/Disconnect.hpp>
#include <service-cellular/service-cellular/CellularServiceAPI.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-audio/AudioServiceAPI.hpp>

extern "C"
{
#include "btstack.h"
#include "btstack_run_loop_freertos.h"
#include "btstack_stdin.h"
#include <btstack_defines.h>
}



@@ 43,7 39,7 @@ namespace bluetooth
        return *this;
    }

    auto HSP::init() -> Error::Code
    auto HSP::init() -> Result::Code
    {
        return pimpl->init();
    }


@@ 53,9 49,8 @@ namespace bluetooth
        pimpl->setDevice(device);
    }

    void HSP::setOwnerService(const sys::Service *service)
    void HSP::setOwnerService(sys::Service *service)
    {
        ownerService = service;
        pimpl->setOwnerService(service);
    }



@@ 69,49 64,102 @@ namespace bluetooth
        pimpl->disconnect();
    }

    auto HSP::startRinging() const noexcept -> Error::Code
    void HSP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        pimpl->setAudioDevice(audioDevice);
    }

    auto HSP::setIncomingCallNumber([[maybe_unused]] const std::string &num) const noexcept -> Result::Code
    {
        pimpl->startRinging();
        return Error::Success;
        return Result::Code::Success;
    }

    auto HSP::stopRinging() const noexcept -> Error::Code
    auto HSP::setSignalStrength([[maybe_unused]] int bars) const noexcept -> Result::Code
    {
        pimpl->stopRinging();
        return Error::Success;
        return Result::Code::Success;
    }

    auto HSP::initializeCall() const noexcept -> Error::Code
    auto HSP::setOperatorName([[maybe_unused]] const std::string_view &name) const noexcept -> Result::Code
    {
        pimpl->initializeCall();
        return Error::Success;
        return Result::Code::Success;
    }

    auto HSP::setBatteryLevel([[maybe_unused]] const BatteryLevel &name) const noexcept -> Result::Code
    {
        return Result::Code::Success;
    }

    auto HSP::incomingCallStarted() const noexcept -> Result::Code
    {
        return pimpl->incomingCallStarted();
    }

    auto HSP::outgoingCallStarted(const std::string &number) const noexcept -> Result::Code
    {
        return pimpl->outgoingCallStarted(number);
    }

    auto HSP::incomingCallAnswered() const noexcept -> Result::Code
    {
        return pimpl->incomingCallAnswered();
    }

    auto HSP::outgoingCallAnswered() const noexcept -> Result::Code
    {
        return pimpl->outgoingCallAnswered();
    }

    auto HSP::callTerminated() const noexcept -> Result::Code
    {
        return pimpl->callTerminated();
    }

    auto HSP::callMissed() const noexcept -> Result::Code
    {
        return pimpl->callMissed();
    }

    auto HSP::setNetworkRegistrationStatus([[maybe_unused]] bool registered) const noexcept -> Result::Code
    {
        return Result::Code::Success;
    }

    auto HSP::setRoamingStatus([[maybe_unused]] bool enabled) const noexcept -> Result::Code
    {
        return Result::Code::Success;
    }

    HSP::~HSP() = default;

    uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
    std::array<char, commandBufferLength> HSP::HSPImpl::ATcommandBuffer;
    std::array<uint8_t, serviceBufferLength> HSP::HSPImpl::serviceBuffer;
    std::uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
    std::uint8_t HSP::HSPImpl::serviceBuffer[serviceBufferSize];

    const char *HSP::HSPImpl::agServiceName = "Mudita Pure HSP";

    std::unique_ptr<SCO> HSP::HSPImpl::sco;
    std::shared_ptr<CVSDAudioDevice> HSP::HSPImpl::audioDevice;

    std::unique_ptr<CellularInterface> HSP::HSPImpl::cellularInterface = nullptr;
    std::unique_ptr<AudioInterface> HSP::HSPImpl::audioInterface       = nullptr;
    const sys::Service *HSP::HSPImpl::ownerService;
    const std::string_view HSP::HSPImpl::agServiceName = "Mudita Pure HSP";
    bool HSP::HSPImpl::isConnected                     = false;
    bool HSP::HSPImpl::callAnswered                    = false;
    bool HSP::HSPImpl::isRinging                       = false;
    std::shared_ptr<CVSDAudioDevice> HSP::HSPImpl::audioDevice;

    sys::Service *HSP::HSPImpl::ownerService;
    Devicei HSP::HSPImpl::device;

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    btstack_packet_callback_registration_t HSP::HSPImpl::hciEventCallbackRegistration;
    char HSP::HSPImpl::ATCommandBuffer[commandBufferSize];
    HSP::HSPImpl::HSPState HSP::HSPImpl::state = HSPState::RfcommDisconnected;

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState deviceState)
    {
        auto evt       = std::make_shared<audio::Event>(event, state);
        auto evt       = std::make_shared<audio::Event>(event, deviceState);
        auto msg       = std::make_shared<AudioEventRequest>(std::move(evt));
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
        ownerService->bus.sendUnicast(std::move(msg), service::name::evt_manager);
    }

    void HSP::HSPImpl::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize)
    void HSP::HSPImpl::packetHandler(std::uint8_t packetType,
                                     [[maybe_unused]] std::uint16_t channel,
                                     std::uint8_t *event,
                                     std::uint16_t eventSize)
    {
        switch (packetType) {
        case HCI_SCO_DATA_PACKET:


@@ 126,12 174,13 @@ namespace bluetooth
        case HCI_EVENT_PACKET:
            processHCIEvent(event);
            break;

        default:
            break;
        }
    }

    void HSP::HSPImpl::processHCIEvent(uint8_t *event)
    void HSP::HSPImpl::processHCIEvent(std::uint8_t *event)
    {
        switch (hci_event_packet_get_type(event)) {
        case HCI_EVENT_SCO_CAN_SEND_NOW:


@@ 142,124 191,123 @@ namespace bluetooth
                sco::utils::sendZeros(scoHandle);
            }
            break;

        case HCI_EVENT_HSP_META:
            processHSPEvent(event);
            break;

        default:
            break;
        }
    }

    void HSP::HSPImpl::processHSPEvent(uint8_t *event)
    void HSP::HSPImpl::processHSPEvent(std::uint8_t *event)
    {
        auto eventDescriptor = event[2];
        switch (eventDescriptor) {
        case HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE:
            if (hsp_subevent_rfcomm_connection_complete_get_status(event) != 0u) {
                LOG_DEBUG("RFCOMM connection establishement failed with status %u\n",
        switch (hci_event_hsp_meta_get_subevent_code(event)) {
        case HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE: {
            if (hsp_subevent_rfcomm_connection_complete_get_status(event) != ERROR_CODE_SUCCESS) {
                LOG_ERROR("RFCOMM connection failed, status: 0x%02X",
                          hsp_subevent_rfcomm_connection_complete_get_status(event));
                sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
                isConnected = false;
                state = HSPState::RfcommDisconnected;
                break;
            }
            LOG_DEBUG("RFCOMM connection established.\n");

            LOG_INFO("RFCOMM connection established");
            sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Connected);
            {
                auto &busProxy     = const_cast<sys::Service *>(ownerService)->bus;
                device.deviceState = DeviceState::ConnectedVoice;
                busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, true),
                                     service::name::bluetooth);
            }
            isConnected = true;
            break;
            state              = HSPState::RfcommConnected;
            device.deviceState = DeviceState::ConnectedVoice;

            using message::bluetooth::ConnectResult;
            ownerService->bus.sendUnicast(std::make_shared<ConnectResult>(device, ConnectResult::Result::Success),
                                          service::name::bluetooth);
        } break;

        case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE: {
            LOG_DEBUG("RFCOMM disconnected.\n");
            LOG_INFO("RFCOMM disconnected");
            sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                 service::name::bluetooth);

            isConnected = false;
            state = HSPState::RfcommDisconnected;
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                          service::name::bluetooth);
        } break;
        case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE:
            if (hsp_subevent_audio_connection_complete_get_status(event) != 0u) {
                LOG_DEBUG("Audio connection establishment failed with status %u\n",
                          hsp_subevent_audio_connection_complete_get_status(event));
                isConnected  = false;
                callAnswered = false;

        case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE: {
            const auto status = hsp_subevent_audio_connection_complete_get_status(event);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_ERROR("Audio connection establishment failed, status: 0x%02X", status);
                state = HSPState::RfcommDisconnected;
                audioDevice.reset();
                break;
            }
            else {
                scoHandle = hsp_subevent_audio_connection_complete_get_sco_handle(event);
                LOG_DEBUG("Audio connection established with SCO handle 0x%04x.\n", scoHandle);
                callAnswered = true;
                hci_request_sco_can_send_now_event();
            }
            break;
        case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE:
            LOG_DEBUG("Audio connection released.\n\n");
            scoHandle    = HCI_CON_HANDLE_INVALID;
            callAnswered = false;
            sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
            break;
        case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED:
            LOG_DEBUG("Received microphone gain change %d\n", hsp_subevent_microphone_gain_changed_get_gain(event));
            break;

            scoHandle = hsp_subevent_audio_connection_complete_get_sco_handle(event);
            LOG_INFO("Audio connection established with SCO handle 0x%04X", scoHandle);
            state = HSPState::Answered;

            audioInterface->startAudioRouting(ownerService);
            hci_request_sco_can_send_now_event();
        } break;

        case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE: {
            scoHandle = HCI_CON_HANDLE_INVALID;
            audioDevice.reset();
            state = HSPState::RfcommConnected;
            LOG_INFO("Audio disconnected");
        } break;

        case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED: {
            LOG_INFO("Received microphone gain change: %d", hsp_subevent_microphone_gain_changed_get_gain(event));
        } break;

        case HSP_SUBEVENT_SPEAKER_GAIN_CHANGED: {
            const auto volume = hsp_subevent_speaker_gain_changed_get_gain(event);
            auto &busProxy    = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::HSPVolume>(volume), service::name::bluetooth);
            LOG_DEBUG("Received speaker gain change %d\n", hsp_subevent_speaker_gain_changed_get_gain(event));
            LOG_INFO("Received speaker gain change: %d", hsp_subevent_speaker_gain_changed_get_gain(event));
            ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::HSPVolume>(volume),
                                          service::name::bluetooth);
        } break;

        case HSP_SUBEVENT_HS_CALL_ANSWER:
            LOG_DEBUG("HSP CALL ANSWER");
            cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
            LOG_INFO("HSP call answer");
            cellularInterface->answerIncomingCall(ownerService);
            break;

        case HSP_SUBEVENT_HS_CALL_HANGUP:
            LOG_DEBUG("HSP CALL HANGUP");
            cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
            LOG_INFO("HSP call hangup");
            cellularInterface->hangupCall(ownerService);
            break;

        case HSP_SUBEVENT_HS_COMMAND: {
            ATcommandBuffer.fill(0);
            auto cmd_length   = hsp_subevent_hs_command_get_value_length(event);
            auto size         = cmd_length <= ATcommandBuffer.size() ? cmd_length : ATcommandBuffer.size();
            auto commandValue = hsp_subevent_hs_command_get_value(event);
            memcpy(ATcommandBuffer.data(), commandValue, size - 1);
            LOG_DEBUG("Received custom command: \"%s\". \nExit code or call hsp_ag_send_result.\n",
                      ATcommandBuffer.data());
            const std::size_t cmdLength = hsp_subevent_hs_command_get_value_length(event);
            const auto size             = std::min(cmdLength, sizeof(ATCommandBuffer));
            const auto commandValue     = hsp_subevent_hs_command_get_value(event);

            std::memset(ATCommandBuffer, 0, sizeof(ATCommandBuffer));
            std::memcpy(ATCommandBuffer, commandValue, size - 1);

            LOG_INFO("Received custom command: '%s'", ATCommandBuffer);
            break;
        }

        case HSP_SUBEVENT_BUTTON_PRESSED: {
            if (scoHandle == HCI_CON_HANDLE_INVALID) {
                if (isRinging) {
                    LOG_DEBUG("Button event -> establish audio");
                    establishAudioConnection();
                    audioInterface->startAudioRouting(const_cast<sys::Service *>(ownerService));
                    cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
                }
            if ((scoHandle == HCI_CON_HANDLE_INVALID) && (state == HSPState::Ringing)) {
                LOG_INFO("Received button event in ringing state, answering call");
                cellularInterface->answerIncomingCall(ownerService);
                break;
            }
            LOG_DEBUG("Button event -> release audio");
            isRinging = false;
            if (callAnswered) {
                cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));

            if (state == HSPState::Answered) {
                LOG_INFO("Received button event in answered state, hanging up call");
                cellularInterface->hangupCall(ownerService);
            }
            callAnswered = false;
            hsp_ag_release_audio_connection();
        } break;

        default:
            LOG_DEBUG("event not handled %u\n", event[2]);
            LOG_INFO("Event not handled: 0x%02X", hci_event_hsp_meta_get_subevent_code(event));
            break;
        }
    }

    void HSP::HSPImpl::establishAudioConnection()
    {
        LOG_DEBUG("Establish Audio connection...\n");
        hsp_ag_establish_audio_connection();
    }

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


@@ 271,33 319,30 @@ namespace bluetooth
        Profile::initL2cap();
        Profile::initSdp();

        serviceBuffer.fill(0);
        constexpr uint32_t hspSdpRecordHandle = 0x10004;
        hsp_ag_create_sdp_record(serviceBuffer.data(), hspSdpRecordHandle, rfcommChannelNr, agServiceName.data());
        std::memset(serviceBuffer, 0, sizeof(serviceBuffer));

        hsp_ag_create_sdp_record(serviceBuffer, hspSdpRecordHandle, rfcommChannelNr, agServiceName);

        if (const auto status = sdp_register_service(serviceBuffer.data()); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service. Status %x", status);
        if (const auto status = sdp_register_service(serviceBuffer); status != ERROR_CODE_SUCCESS) {
            LOG_ERROR("Can't register service, status: 0x%02X", status);
        }

        rfcomm_init();

        hsp_ag_init(rfcommChannelNr);
        hsp_ag_register_packet_handler(&packetHandler);

        // register for SCO packets
        hciEventCallbackRegistration.callback = &packetHandler;
        hci_add_event_handler(&hciEventCallbackRegistration);
        hsp_ag_register_packet_handler(&packetHandler);
        hci_register_sco_packet_handler(&packetHandler);

        gap_discoverable_control(1);
        gap_set_class_of_device(CLASS_OF_DEVICE);

        LOG_INFO("HSP init done!");

        return bluetooth::Error::Success;
        LOG_INFO("HSP initialized!");
        return bluetooth::Result::Code::Success;
    }

    void HSP::HSPImpl::connect()
    {
        if (isConnected) {
        if (state != HSPState::RfcommDisconnected) {
            disconnect();
        }
        hsp_ag_connect(device.address);


@@ 307,87 352,66 @@ namespace bluetooth
    {
        hsp_ag_release_audio_connection();
        hsp_ag_disconnect();
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device), service::name::bluetooth);
        ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                      service::name::bluetooth);
    }

    void HSP::HSPImpl::setDevice(const Devicei &dev)
    {
        device = dev;
        LOG_INFO("Device set!");
        LOG_DEBUG("Device set!");
    }

    void HSP::HSPImpl::setOwnerService(const sys::Service *service)
    void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDev)
    {
        ownerService = service;
        HSP::HSPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDev);
    }

    void HSP::HSPImpl::startRinging() const noexcept
    void HSP::HSPImpl::setOwnerService(sys::Service *service)
    {
        LOG_DEBUG("Bluetooth ring started");
        hsp_ag_start_ringing();
        isRinging = true;
        ownerService = service;
    }

    void HSP::HSPImpl::stopRinging() const noexcept
    auto HSP::HSPImpl::incomingCallStarted() const noexcept -> Result::Code
    {
        LOG_DEBUG("Bluetooth ring stopped");
        hsp_ag_stop_ringing();
        isRinging = false;
        LOG_DEBUG("Incoming call started");
        hsp_ag_start_ringing();
        state = HSPState::Ringing;
        return Result::Code::Success;
    }

    void HSP::HSPImpl::initializeCall() const noexcept
    auto HSP::HSPImpl::outgoingCallStarted([[maybe_unused]] const std::string &number) const noexcept -> Result::Code
    {
        stopRinging();
        establishAudioConnection();
        LOG_DEBUG("Outgoing call started");
        hsp_ag_establish_audio_connection();
        return Result::Code::Success;
    }

    void HSP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    {
        pimpl->setAudioDevice(audioDevice);
    }
    auto HSP::callActive() const noexcept -> Error::Code
    {
        return Error::Success;
    }
    auto HSP::setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code
    {
        return Error::Success;
    }
    auto HSP::setSignalStrength(int bars) const noexcept -> Error::Code
    {
        return Error::Success;
    }
    auto HSP::setOperatorName(const std::string_view &name) const noexcept -> Error::Code
    {
        return Error::Success;
    }
    auto HSP::setBatteryLevel(const BatteryLevel &name) const noexcept -> Error::Code
    {
        return Error::Success;
    }
    auto HSP::terminateCall() const noexcept -> Error::Code
    {
        return pimpl->terminateCall();
    }
    auto HSP::setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code
    auto HSP::HSPImpl::incomingCallAnswered() const noexcept -> Result::Code
    {
        return Error::Success;
        LOG_DEBUG("Incoming call answered");
        hsp_ag_stop_ringing();
        hsp_ag_establish_audio_connection();
        return Result::Code::Success;
    }
    auto HSP::setRoamingStatus(bool enabled) const noexcept -> Error::Code

    auto HSP::HSPImpl::outgoingCallAnswered() const noexcept -> Result::Code
    {
        return Error::Success;
        LOG_DEBUG("Outgoing call answered");
        return Result::Code::Success;
    }

    void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
    auto HSP::HSPImpl::callTerminated() const noexcept -> Result::Code
    {
        HSP::HSPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDevice);
        LOG_DEBUG("Call terminated");
        hsp_ag_stop_ringing();
        hsp_ag_release_audio_connection();
        state = HSPState::RfcommConnected;
        return Result::Code::Success;
    }
    auto HSP::HSPImpl::terminateCall() const noexcept -> Error::Code

    auto HSP::HSPImpl::callMissed() const noexcept -> Result::Code
    {
        stopRinging();
        hsp_ag_release_audio_connection();
        callAnswered = false;
        return Error::Success;
        return callTerminated();
    }
} // namespace bluetooth

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

#pragma once


@@ 12,8 12,6 @@ namespace bluetooth
{
    class HSP : public CallProfile
    {
        static constexpr auto CLASS_OF_DEVICE = 0x400204;
        // Service class: Telephony, Major device class: Phone, Minor device class: Cellular
      public:
        HSP();
        ~HSP() override;


@@ 23,44 21,35 @@ namespace bluetooth
        HSP(HSP &&other) noexcept;
        auto operator=(HSP &&other) noexcept -> HSP &;

        auto init() -> Error::Code override;
        auto init() -> Result::Code override;
        void setDevice(const Devicei &device) override;
        void setOwnerService(const sys::Service *service) override;
        void setOwnerService(sys::Service *service) override;

        void connect() override;
        void disconnect() 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;
        [[nodiscard]] auto terminateCall() const noexcept -> Error::Code override;
        [[nodiscard]] auto callActive() const noexcept -> Error::Code override;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code override;
        [[nodiscard]] auto incomingCallStarted() const noexcept -> Result::Code override;
        [[nodiscard]] auto outgoingCallStarted(const std::string &number) const noexcept -> Result::Code override;
        [[nodiscard]] auto incomingCallAnswered() const noexcept -> Result::Code override;
        [[nodiscard]] auto outgoingCallAnswered() const noexcept -> Result::Code override;
        [[nodiscard]] auto callTerminated() const noexcept -> Result::Code override;
        [[nodiscard]] auto callMissed() const noexcept -> Result::Code override;
        [[nodiscard]] auto setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code override;
        /// @return Success - ignoring in HSP
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Error::Code override;
        [[nodiscard]] auto setSignalStrength(int bars) const noexcept -> Result::Code override;
        /// @return Success - ignoring in HSP
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Error::Code override;
        [[nodiscard]] auto setOperatorName(const std::string_view &name) const noexcept -> Result::Code override;
        /// @return Success - ignoring in HSP
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code override;
        [[nodiscard]] auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code override;
        /// @return Success - ignoring in HSP
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code override;
        [[nodiscard]] auto setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code override;
        /// @return Success - ignoring in HSP
        auto setRoamingStatus(bool enabled) const noexcept -> Error::Code override;
        auto setRoamingStatus(bool enabled) const noexcept -> Result::Code override;

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

      private:
        class HSPImpl;
        std::unique_ptr<HSPImpl> pimpl;
        const sys::Service *ownerService{};
        btstack_run_loop *runLoopInstance{};
    };

} // namespace bluetooth

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

#pragma once

#include "HSP.hpp"
#include "Error.hpp"
#include "Result.hpp"
#include <interface/profiles/SCO/SCO.hpp>
#include <Audio/AudioCommon.hpp>

namespace bluetooth
{
    static constexpr int serviceBufferLength = 150;
    static constexpr int commandBufferLength = 150;

    class HSP::HSPImpl
    {
      public:
        static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize);
        auto init() -> Error::Code;
        void startRinging() const noexcept;
        void stopRinging() const noexcept;
        void initializeCall() const noexcept;
        auto terminateCall() const noexcept -> Error::Code;
        enum class HSPState
        {
            RfcommDisconnected,
            RfcommConnected,
            Ringing,
            Answered
        };

        static void packetHandler(std::uint8_t packetType,
                                  std::uint16_t channel,
                                  std::uint8_t *event,
                                  std::uint16_t eventSize);
        auto init() -> Result::Code;
        auto incomingCallStarted() const noexcept -> Result::Code;
        auto outgoingCallStarted(const std::string &number) const noexcept -> Result::Code;
        auto incomingCallAnswered() const noexcept -> Result::Code;
        auto outgoingCallAnswered() const noexcept -> Result::Code;
        auto callTerminated() const noexcept -> Result::Code;
        auto callMissed() const noexcept -> Result::Code;
        void connect();
        void disconnect();
        void setDevice(const Devicei &dev);
        void setOwnerService(const sys::Service *service);
        void setOwnerService(sys::Service *service);
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);

      private:
        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 const std::string_view agServiceName;
        static uint16_t scoHandle;
        static void processHCIEvent(std::uint8_t *event);
        static void processHSPEvent(std::uint8_t *event);

        static constexpr auto serviceBufferSize           = 150;
        static constexpr auto commandBufferSize           = 150;
        static constexpr std::uint8_t rfcommChannelNr     = 1;
        static constexpr std::uint32_t hspSdpRecordHandle = 0x10004;

        static hci_con_handle_t scoHandle;

        static std::uint8_t serviceBuffer[serviceBufferSize];

        static const char *agServiceName;

        static std::unique_ptr<SCO> sco;
        static std::shared_ptr<CVSDAudioDevice> audioDevice;

        static std::unique_ptr<CellularInterface> cellularInterface;
        static std::unique_ptr<AudioInterface> audioInterface;
        static std::array<char, commandBufferLength> ATcommandBuffer;
        static const sys::Service *ownerService;
        static bool isConnected;
        static bool callAnswered;
        static bool isRinging;
        static std::shared_ptr<CVSDAudioDevice> audioDevice;

        static sys::Service *ownerService;
        static Devicei device;

        static btstack_packet_callback_registration_t hciEventCallbackRegistration;
        static char ATCommandBuffer[commandBufferSize];
        static HSPState state;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +24 -24
@@ 1,9 1,9 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Error.hpp"
#include "Result.hpp"
#include <audio/BluetoothAudioDevice.hpp>
#include <Service/Message.hpp>



@@ 13,14 13,13 @@

namespace bluetooth
{

    class Profile
    {
      public:
        virtual ~Profile()                                                                        = default;
        virtual auto init() -> Error::Code                                                        = 0;
        virtual auto init() -> Result::Code                                                       = 0;
        virtual void setDevice(const Devicei &device)                                             = 0;
        virtual void setOwnerService(const sys::Service *service)                                 = 0;
        virtual void setOwnerService(sys::Service *service)                                       = 0;
        virtual void connect()                                                                    = 0;
        virtual void disconnect()                                                                 = 0;
        virtual void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) = 0;


@@ 44,41 43,42 @@ namespace bluetooth
    class CallProfile : public Profile
    {
      public:
        [[nodiscard]] virtual auto startRinging() const noexcept -> Error::Code = 0;
        /// Stops ringing
        /// Executed after incoming call has been received
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto stopRinging() const noexcept -> Error::Code = 0;
        /// Initializes call
        [[nodiscard]] virtual auto incomingCallStarted() const noexcept -> Result::Code = 0;
        /// Executed after outgoing call has been placed
        /// @param outgoing call number
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto initializeCall() const noexcept -> Error::Code = 0;
        /// Terminates call
        [[nodiscard]] virtual auto outgoingCallStarted(const std::string &number) const noexcept -> Result::Code = 0;
        /// Executed after incoming call is answered
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto terminateCall() const noexcept -> Error::Code = 0;
        /// Executed after the call is answered
        [[nodiscard]] virtual auto incomingCallAnswered() const noexcept -> Result::Code = 0;
        /// Executed after outgoing call is answered
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto callActive() const noexcept -> Error::Code = 0;
        /// Executed after the call has been started
        /// @param outgoing call number
        [[nodiscard]] virtual auto outgoingCallAnswered() const noexcept -> Result::Code = 0;
        /// Executed after termination of the ongoing call
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto callStarted(const std::string &num) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto callTerminated() const noexcept -> Result::Code = 0;
        /// Executed after termination of the incoming call that has not yet been answered
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto callMissed() const noexcept -> Result::Code = 0;
        /// Sets the incoming call number
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto setIncomingCallNumber(const std::string &num) const noexcept -> Result::Code = 0;
        /// Sets the signal strength bars data in HFP profile
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto setSignalStrength(int bars) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto setSignalStrength(int bars) const noexcept -> Result::Code = 0;
        /// Sets the operator name in HFP profile
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto setOperatorName(const std::string_view &name) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto setOperatorName(const std::string_view &name) const noexcept -> Result::Code = 0;
        /// Sets the operator name in HFP profile
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto setBatteryLevel(const BatteryLevel &level) const noexcept -> Result::Code = 0;
        /// Sets the network registration status in HFP profile
        /// @return Error code that determines, whether operation was successful or not
        [[nodiscard]] virtual auto setNetworkRegistrationStatus(bool registered) const noexcept -> Error::Code = 0;
        [[nodiscard]] virtual auto setNetworkRegistrationStatus(bool registered) const noexcept -> Result::Code = 0;
        /// Sets the roaming status in HFP profile
        /// @return Error code that determines, whether operation was successful or not
        virtual auto setRoamingStatus(bool enabled) const noexcept -> Error::Code = 0;
        virtual auto setRoamingStatus(bool enabled) const noexcept -> Result::Code = 0;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +53 -46
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-bluetooth/ServiceBluetooth.hpp>


@@ 7,11 7,10 @@

namespace bluetooth
{

    ProfileManager::ProfileManager(sys::Service *ownerService) : ownerService(ownerService)
    {}

    auto ProfileManager::init() -> Error::Code
    auto ProfileManager::init() -> Result::Code
    {
        if (!initialized) {
            profilesList = {{AudioProfile::A2DP, std::make_shared<bluetooth::A2DP>()},


@@ 34,10 33,10 @@ namespace bluetooth
            }
            initialized = true;
        }
        return Error::Success;
        return Result::Code::Success;
    }

    auto ProfileManager::connect(const Devicei &device) -> Error::Code
    auto ProfileManager::connect(const Devicei &device) -> Result::Code
    {
        for (auto &[profileName, ptr] : profilesList) {
            if (ptr != nullptr) {


@@ 45,102 44,118 @@ namespace bluetooth
                ptr->connect();
            }
        }
        return Error::Success;
        return Result::Code::Success;
    }

    auto ProfileManager::disconnect() -> Error::Code
    auto ProfileManager::disconnect() -> Result::Code
    {
        for (auto &[profileName, ptr] : profilesList) {
            if (ptr != nullptr) {
                ptr->disconnect();
            }
        }
        return Error::Success;
        return Result::Code::Success;
    }

    auto ProfileManager::start() -> Error::Code
    auto ProfileManager::start() -> Result::Code
    {
        musicProfilePtr->start();
        return Error::Success;
        return Result::Code::Success;
    }

    auto ProfileManager::stop() -> Error::Code
    auto ProfileManager::stop() -> Result::Code
    {
        musicProfilePtr->stop();
        return Error::Success;
        return Result::Code::Success;
    }

    auto ProfileManager::startRinging() -> Error::Code
    auto ProfileManager::incomingCallStarted() -> Result::Code
    {
        return callProfilePtr->startRinging();
        return callProfilePtr->incomingCallStarted();
    }

    auto ProfileManager::stopRinging() -> Error::Code
    auto ProfileManager::outgoingCallStarted(const utils::PhoneNumber &nr) -> Result::Code
    {
        return callProfilePtr->stopRinging();
        if (callProfilePtr) {
            return callProfilePtr->outgoingCallStarted(nr.getView().getNonEmpty());
        }
        LOG_ERROR("No profile, returning!");
        return Result::Code::NotReady;
    }

    auto ProfileManager::initializeCall() -> Error::Code
    auto ProfileManager::incomingCallAnswered() -> Result::Code
    {
        return callProfilePtr->initializeCall();
        return callProfilePtr->incomingCallAnswered();
    }
    auto ProfileManager::terminateCall() -> Error::Code

    auto ProfileManager::outgoingCallAnswered() -> Result::Code
    {
        return callProfilePtr->terminateCall();
        return callProfilePtr->outgoingCallAnswered();
    }

    auto ProfileManager::setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code
    auto ProfileManager::callTerminated() -> Result::Code
    {
        return callProfilePtr->callTerminated();
    }

    auto ProfileManager::callMissed() -> Result::Code
    {
        return callProfilePtr->callMissed();
    }

    auto ProfileManager::setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Result::Code
    {
        auto profileType = device->getProfileType();

        if (profilesList[profileType] == nullptr) {
            return Error::NotReady;
            return Result::Code::NotReady;
        }

        profilesList[profileType]->setAudioDevice(device);
        LOG_ERROR("AudioDevice for profile: %s set!", magic_enum::enum_name(profileType).data());
        return Error::Success;
    }
    auto ProfileManager::callAnswered() -> Error::Code
    {
        return callProfilePtr->callActive();
        return Result::Code::Success;
    }
    auto ProfileManager::setIncomingCallNumber(const utils::PhoneNumber &nr) -> Error::Code

    auto ProfileManager::setIncomingCallNumber(const utils::PhoneNumber &nr) -> Result::Code
    {
        if (callProfilePtr) {
            return callProfilePtr->setIncomingCallNumber(nr.getView().getE164());
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
        return Result::Code::NotReady;
    }
    auto ProfileManager::setSignalStrengthData(const DataVariant &data) -> Error::Code

    auto ProfileManager::setSignalStrengthData(const DataVariant &data) -> Result::Code
    {
        auto signalData = std::get<Store::SignalStrength>(data);
        if (callProfilePtr) {
            return callProfilePtr->setSignalStrength(static_cast<int>(signalData.rssiBar));
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
        return Result::Code::NotReady;
    }
    auto ProfileManager::setOperatorNameData(const DataVariant &data) -> Error::Code

    auto ProfileManager::setOperatorNameData(const DataVariant &data) -> Result::Code
    {
        auto operatorName = std::get<OperatorName>(data);
        if (callProfilePtr) {
            return callProfilePtr->setOperatorName(operatorName.getName());
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
        return Result::Code::NotReady;
    }
    auto ProfileManager::setBatteryLevelData(unsigned int level) -> Error::Code

    auto ProfileManager::setBatteryLevelData(unsigned int level) -> Result::Code
    {
        auto batteryLevel = BatteryLevel(level);
        if (callProfilePtr) {
            return callProfilePtr->setBatteryLevel(batteryLevel);
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
        return Result::Code::NotReady;
    }
    auto ProfileManager::setNetworkStatusData(const DataVariant &data) -> Error::Code

    auto ProfileManager::setNetworkStatusData(const DataVariant &data) -> Result::Code
    {
        auto status = std::get<Store::Network::Status>(data);
        if (callProfilePtr) {


@@ 155,16 170,9 @@ namespace bluetooth
            }
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
    }
    auto ProfileManager::callStarted(const utils::PhoneNumber &nr) -> Error::Code
    {
        if (callProfilePtr) {
            return callProfilePtr->callStarted(nr.getView().getE164());
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
        return Result::Code::NotReady;
    }

    void ProfileManager::deInit()
    {
        for (auto &[profileName, ptr] : profilesList) {


@@ 178,5 186,4 @@ namespace bluetooth

        LOG_DEBUG("ProfileManager deinit done");
    }

} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +36 -36
@@ 1,10 1,10 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Service.hpp>
#include <Error.hpp>
#include "Result.hpp"
#include "Profile.hpp"
#include "AudioProfile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"


@@ 30,24 30,24 @@ namespace bluetooth
    {
      public:
        virtual ~BaseProfileManager()                                                            = default;
        virtual auto init() -> Error::Code                                                       = 0;
        virtual auto init() -> Result::Code                                                       = 0;
        virtual void deInit()                                                                    = 0;
        virtual auto connect(const Devicei &device) -> Error::Code                               = 0;
        virtual auto disconnect() -> Error::Code                                                 = 0;
        virtual auto start() -> Error::Code                                                      = 0;
        virtual auto stop() -> Error::Code                                                       = 0;
        virtual auto startRinging() -> Error::Code                                               = 0;
        virtual auto stopRinging() -> Error::Code                                                = 0;
        virtual auto initializeCall() -> Error::Code                                             = 0;
        virtual auto terminateCall() -> Error::Code                                              = 0;
        virtual auto callAnswered() -> Error::Code                                               = 0;
        virtual auto callStarted(const utils::PhoneNumber &) -> Error::Code                      = 0;
        virtual auto setIncomingCallNumber(const utils::PhoneNumber &nr) -> Error::Code          = 0;
        virtual auto setSignalStrengthData(const DataVariant &data) -> Error::Code               = 0;
        virtual auto setOperatorNameData(const DataVariant &data) -> Error::Code                 = 0;
        virtual auto setBatteryLevelData(unsigned int) -> Error::Code                            = 0;
        virtual auto setNetworkStatusData(const DataVariant &data) -> Error::Code                = 0;
        virtual auto setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Error::Code = 0;
        virtual auto connect(const Devicei &device) -> Result::Code                               = 0;
        virtual auto disconnect() -> Result::Code                                                 = 0;
        virtual auto start() -> Result::Code                                                      = 0;
        virtual auto stop() -> Result::Code                                                       = 0;
        virtual auto incomingCallStarted() -> Result::Code                                        = 0;
        virtual auto outgoingCallStarted(const utils::PhoneNumber &) -> Result::Code              = 0;
        virtual auto incomingCallAnswered() -> Result::Code                                       = 0;
        virtual auto outgoingCallAnswered() -> Result::Code                                       = 0;
        virtual auto callTerminated() -> Result::Code                                             = 0;
        virtual auto callMissed() -> Result::Code                                                 = 0;
        virtual auto setIncomingCallNumber(const utils::PhoneNumber &nr) -> Result::Code          = 0;
        virtual auto setSignalStrengthData(const DataVariant &data) -> Result::Code               = 0;
        virtual auto setOperatorNameData(const DataVariant &data) -> Result::Code                 = 0;
        virtual auto setBatteryLevelData(unsigned int) -> Result::Code                            = 0;
        virtual auto setNetworkStatusData(const DataVariant &data) -> Result::Code                = 0;
        virtual auto setAudioDevice(std::shared_ptr<BluetoothAudioDevice> device) -> Result::Code = 0;
    };

    class ProfileManager : public BaseProfileManager


@@ 56,25 56,25 @@ namespace bluetooth
        explicit ProfileManager(sys::Service *ownerService);
        ProfileManager() = delete;

        auto init() -> Error::Code override;
        auto init() -> Result::Code override;
        void deInit() override;
        auto connect(const Devicei &device) -> Error::Code override;
        auto disconnect() -> Error::Code override;
        auto start() -> Error::Code override;
        auto stop() -> Error::Code override;
        auto startRinging() -> Error::Code override;
        auto stopRinging() -> Error::Code override;
        auto initializeCall() -> Error::Code override;
        auto terminateCall() -> Error::Code override;
        auto callAnswered() -> Error::Code override;
        auto callStarted(const utils::PhoneNumber &) -> Error::Code override;
        auto setIncomingCallNumber(const utils::PhoneNumber &nr) -> Error::Code override;
        auto setSignalStrengthData(const DataVariant &data) -> Error::Code override;
        auto setOperatorNameData(const DataVariant &data) -> Error::Code override;
        auto setBatteryLevelData(unsigned int) -> Error::Code override;
        auto setNetworkStatusData(const DataVariant &data) -> Error::Code override;
        auto connect(const Devicei &device) -> Result::Code override;
        auto disconnect() -> Result::Code override;
        auto start() -> Result::Code override;
        auto stop() -> Result::Code override;
        auto incomingCallStarted() -> Result::Code override;
        auto outgoingCallStarted(const utils::PhoneNumber &nr) -> Result::Code override;
        auto incomingCallAnswered() -> Result::Code override;
        auto outgoingCallAnswered() -> Result::Code override;
        auto callTerminated() -> Result::Code override;
        auto callMissed() -> Result::Code override;
        auto setIncomingCallNumber(const utils::PhoneNumber &nr) -> Result::Code override;
        auto setSignalStrengthData(const DataVariant &data) -> Result::Code override;
        auto setOperatorNameData(const DataVariant &data) -> Result::Code override;
        auto setBatteryLevelData(unsigned int level) -> Result::Code override;
        auto setNetworkStatusData(const DataVariant &data) -> Result::Code override;

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

      private:
        sys::Service *ownerService;

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

#include "SCO.hpp"


@@ 46,7 46,7 @@ namespace bluetooth
        static const sys::Service *ownerService;
        static uint8_t negotiated_codec;

        static auto audioInitialize(int sampleRate) -> Error;
        static auto audioInitialize(int sampleRate) -> Result;
        static void initCvsd();
        static void receiveCvsd(uint8_t *packet, uint16_t size);
        static void writeToHostEndian(int16_t *buffer, uint8_t *packet, int length);


@@ 110,7 110,7 @@ void SCO::SCOImpl::sendEvent(audio::EventType event, audio::Event::DeviceState s
    auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
    busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
}
auto SCO::SCOImpl::audioInitialize(int sampleRate) -> Error
auto SCO::SCOImpl::audioInitialize(int sampleRate) -> Result
{
    sourceQueue = xQueueCreate(5, sizeof(AudioData_t));
    sinkQueue   = xQueueCreate(5, sizeof(AudioData_t));


@@ 124,18 124,18 @@ auto SCO::SCOImpl::audioInitialize(int sampleRate) -> Error

    if (sourceQueue == nullptr || sinkQueue == nullptr) {
        LOG_ERROR("failed to create queue!");
        return Error(Error::SystemError);
        return Result(Result::Code::SystemError);
    }

    LOG_INFO("Init done!");
    return Error(Error::Success);
    return Result(Result::Code::Success);
}

void SCO::SCOImpl::initCvsd()
{
    btstack_cvsd_plc_init(&cvsdPlcState);
    auto ret = audioInitialize(CVSD_SAMPLE_RATE);
    if (ret.err == Error::Success) {
    if (ret.result == Result::Code::Success) {
        LOG_INFO("CVSD init done!");
    }
}

M module-bluetooth/tests/tests-StatefulController.cpp => module-bluetooth/tests/tests-StatefulController.cpp +116 -59
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 8,7 8,7 @@

using namespace bluetooth;

auto InitializerMock = []() { return Error::Success; };
auto InitializerMock = []() { return Result::Code::Success; };

namespace mock
{


@@ 22,10 22,10 @@ namespace mock
    auto driver()
    {
        fakeit::Mock<AbstractDriver> mock;
        fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, run)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stop)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, init)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, run)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, stop)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, scan)).AlwaysReturn(Result());
        fakeit::When(Method(mock, stopScan)).AlwaysReturn();
        fakeit::When(Method(mock, setVisibility)).AlwaysReturn();
        fakeit::When(Method(mock, pair)).AlwaysReturn();


@@ 38,14 38,14 @@ namespace mock
    auto handler()
    {
        fakeit::Mock<AbstractCommandHandler> mock;
        fakeit::When(Method(mock, scan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stopScan)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setVisibility)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, connect)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, disconnect)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, pair)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, unpair)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, availableDevices)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, scan)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, stopScan)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setVisibility)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, connect)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, disconnect)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, pair)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, unpair)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, availableDevices)).AlwaysReturn(Result::Code::Success);
        return mock;
    };



@@ 65,24 65,24 @@ namespace mock
    auto profile()
    {
        fakeit::Mock<bluetooth::BaseProfileManager> mock;
        fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, init)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, deInit)).AlwaysReturn();
        fakeit::When(Method(mock, connect)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, disconnect)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, start)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stop)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, startRinging)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, stopRinging)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, initializeCall)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, terminateCall)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, callAnswered)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, callStarted)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setIncomingCallNumber)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setSignalStrengthData)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setOperatorNameData)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setBatteryLevelData)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setNetworkStatusData)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, setAudioDevice)).AlwaysReturn(Error::Code::Success);
        fakeit::When(Method(mock, connect)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, disconnect)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, start)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, stop)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, incomingCallStarted)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, outgoingCallStarted)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, incomingCallAnswered)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, outgoingCallAnswered)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, callTerminated)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, callMissed)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setIncomingCallNumber)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setSignalStrengthData)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setOperatorNameData)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setBatteryLevelData)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setNetworkStatusData)).AlwaysReturn(Result::Code::Success);
        fakeit::When(Method(mock, setAudioDevice)).AlwaysReturn(Result::Code::Success);
        return mock;
    };



@@ 198,70 198,127 @@ TEST_CASE("call test - incoming call")
    REQUIRE(controller.sm.is(state<bluetooth::On>));
    auto number = utils::PhoneNumber::View{};

    SECTION("dropped call with no number event")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallStarted{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("answered call with no number event")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallStarted{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallAnswered{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInProgress"_s));
        fakeit::Verify(Method(sm.p, incomingCallAnswered)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("missed call with no number event")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallStarted{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallMissed{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, callMissed)).Exactly(1);
    }

    SECTION("delayed incoming call number event")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallStarted{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("dropped call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("answered call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallAnswered{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInProgress"_s));
        fakeit::Verify(Method(sm.p, callAnswered)).Exactly(1);
        fakeit::Verify(Method(sm.p, incomingCallAnswered)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("missed call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::StopRinging{}));
        REQUIRE(controller.sm.process_event(bluetooth::event::CallMissed{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, callMissed)).Exactly(1);
    }

    SECTION("double missed call")
    {
        LOG_INFO("Double missed call");
        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::StopRinging{}));
        REQUIRE(controller.sm.process_event(bluetooth::event::CallMissed{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(1);
        fakeit::Verify(Method(sm.p, callMissed)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::IncomingCallNumber{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallRinging"_s));
        fakeit::Verify(Method(sm.p, startRinging)).Exactly(2);
        fakeit::Verify(Method(sm.p, incomingCallStarted)).Exactly(2);
        fakeit::Verify(Method(sm.p, setIncomingCallNumber)).Exactly(2);

        REQUIRE(controller.sm.process_event(bluetooth::event::StopRinging{}));
        REQUIRE(controller.sm.process_event(bluetooth::event::CallMissed{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, stopRinging)).Exactly(2);
        fakeit::Verify(Method(sm.p, callMissed)).Exactly(2);
    }
}



@@ 277,49 334,49 @@ TEST_CASE("call test - outgoing call")

    SECTION("dropped call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::CallStarted{number}));
        REQUIRE(controller.sm.process_event(bluetooth::event::OutgoingCallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, outgoingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("answered call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::CallStarted{number}));
        REQUIRE(controller.sm.process_event(bluetooth::event::OutgoingCallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, outgoingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallAnswered{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInProgress"_s));
        fakeit::Verify(Method(sm.p, callAnswered)).Exactly(1);
        fakeit::Verify(Method(sm.p, outgoingCallAnswered)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }

    SECTION("miss-clicked call")
    {
        REQUIRE(controller.sm.process_event(bluetooth::event::CallStarted{number}));
        REQUIRE(controller.sm.process_event(bluetooth::event::OutgoingCallStarted{number}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>("CallInitiated"_s));
        fakeit::Verify(Method(sm.p, callStarted)).Exactly(1);
        fakeit::Verify(Method(sm.p, outgoingCallStarted)).Exactly(1);

        REQUIRE(controller.sm.process_event(bluetooth::event::CallTerminated{}));
        REQUIRE(controller.sm.is<decltype(state<Call>)>(sml::X));
        REQUIRE(controller.sm.is(state<bluetooth::On>));
        fakeit::Verify(Method(sm.p, terminateCall)).Exactly(1);
        fakeit::Verify(Method(sm.p, callTerminated)).Exactly(1);
    }
}

TEST_CASE("Given StatefulController when error during device registration then turned off")
{
    using namespace boost::sml;
    auto sm         = mock::SM([]() { return bluetooth::Error::Code::SystemError; });
    auto sm         = mock::SM([]() { return bluetooth::Result::Code::SystemError; });
    auto controller = sm.get();

    controller.sm.process_event(bluetooth::event::PowerOn{});


@@ 332,7 389,7 @@ TEST_CASE("Given StatefulController when error during driver init then turned of
    auto sm         = mock::SM();
    auto controller = sm.get();

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::SystemError);
    fakeit::When(Method(sm.d, init)).AlwaysReturn(Result::Code::SystemError);

    controller.sm.process_event(bluetooth::event::PowerOn{});
    REQUIRE(!controller.sm.is(state<bluetooth::On>));


@@ 389,11 446,11 @@ TEST_CASE("Given StatefulController when processing command failed then restarte
    auto sm         = mock::SM();
    auto controller = sm.get();

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::SystemError);
    fakeit::When(Method(sm.d, init)).AlwaysReturn(Result::Code::SystemError);
    controller.sm.process_event(bluetooth::event::PowerOn{});
    REQUIRE(!controller.sm.is(state<bluetooth::On>));

    fakeit::When(Method(sm.d, init)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(sm.d, init)).AlwaysReturn(Result::Code::Success);
    controller.sm.process_event(bluetooth::event::PowerOn{});
    REQUIRE(controller.sm.is(state<bluetooth::On>));
}

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +44 -36
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "service-bluetooth/ServiceBluetooth.hpp"


@@ 11,7 11,6 @@
#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/DeviceName.hpp>
#include "service-bluetooth/messages/Disconnect.hpp"


@@ 44,8 43,7 @@

namespace
{
    constexpr auto BluetoothServiceStackDepth = 2560U;
    inline constexpr auto nameSettings        = "ApplicationSettings";
    constexpr auto BluetoothServiceStackDepth = 1024 * 3;
    inline constexpr auto connectionTimeout   = std::chrono::minutes{10};
    inline constexpr auto btRestartDelay      = std::chrono::milliseconds{500};



@@ 95,7 93,6 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    connectHandler<message::bluetooth::A2DPVolume>();
    connectHandler<message::bluetooth::HSPVolume>();
    connectHandler<message::bluetooth::HFPVolume>();
    connectHandler<message::bluetooth::StartAudioRouting>();
    connectHandler<message::bluetooth::Connect>();
    connectHandler<message::bluetooth::ConnectResult>();
    connectHandler<message::bluetooth::Disconnect>();


@@ 114,6 111,7 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    connectHandler<cellular::CallerIdMessage>();
    connectHandler<cellular::IncomingCallMessage>();
    connectHandler<cellular::CallEndedNotification>();
    connectHandler<cellular::CallMissedNotification>();
    connectHandler<cellular::CallStartedNotification>();
    connectHandler<cellular::SignalStrengthUpdateNotification>();
    connectHandler<cellular::CurrentOperatorNameNotification>();


@@ 319,7 317,7 @@ auto convertDeviceStateIntoBluetoothState(const DeviceState &state) -> sys::blue

auto ServiceBluetooth::handle(message::bluetooth::ConnectResult *msg) -> std::shared_ptr<sys::Message>
{
    if (msg->isSucceed()) {
    if (msg->getResult() == message::bluetooth::ConnectResult::Result::Success) {
        auto device = msg->getDevice();
        bluetoothDevicesModel->mergeInternalDeviceState(device);



@@ 466,6 464,7 @@ auto ServiceBluetooth::handle(message::bluetooth::HSPVolume *msg) -> std::shared
    AudioServiceAPI::BluetoothHSPVolumeChanged(this, msg->getVolume());
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::HFPVolume *msg) -> std::shared_ptr<sys::Message>
{
    using namespace message::bluetooth;


@@ 473,26 472,6 @@ auto ServiceBluetooth::handle(message::bluetooth::HFPVolume *msg) -> std::shared
    return sys::MessageNone{};
}

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

auto ServiceBluetooth::handle(cellular::CallerIdMessage *msg) -> std::shared_ptr<sys::Message>
{
    auto number = msg->number;
    auto btOn   = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::State));
    LOG_DEBUG("Received caller ID msg! ");

    if (btOn) {
        LOG_DEBUG("Sending to profile!");
        sendWorkerCommand(std::make_unique<bluetooth::event::IncomingCallNumber>(number));
    }

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::SignalStrengthUpdateNotification *msg) -> std::shared_ptr<sys::Message>
{
    auto signalStrength = Store::GSM::get()->getSignalStrength();


@@ 531,6 510,7 @@ void ServiceBluetooth::handleTurnOff()
    bus.sendMulticast(std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Disabled),
                      sys::BusChannel::BluetoothModeChanges);
}

void ServiceBluetooth::handleTurnOn()
{
    cpuSentinel->HoldMinimumFrequency(bsp::CpuFrequencyMHz::Level_3);


@@ 538,6 518,7 @@ void ServiceBluetooth::handleTurnOn()
    bus.sendMulticast(std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Enabled),
                      sys::BusChannel::BluetoothModeChanges);
}

auto ServiceBluetooth::handle(message::bluetooth::RequestStatusIndicatorData *msg) -> std::shared_ptr<sys::Message>
{
    bus.sendUnicast(std::make_shared<cellular::RequestCurrentOperatorNameMessage>(), cellular::service::name);


@@ 549,6 530,7 @@ auto ServiceBluetooth::handle(message::bluetooth::RequestStatusIndicatorData *ms

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(sevm::BatteryStatusChangeMessage *msg) -> std::shared_ptr<sys::Message>
{
    auto batteryLevel = Store::Battery::get().level;


@@ 556,11 538,7 @@ auto ServiceBluetooth::handle(sevm::BatteryStatusChangeMessage *msg) -> std::sha
    sendWorkerCommand(std::make_unique<bluetooth::event::BatteryLevelData>(batteryLevel));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallEndedNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(std::make_unique<bluetooth::event::CallTerminated>());
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::NetworkStatusUpdateNotification *msg) -> std::shared_ptr<sys::Message>
{
    auto status = Store::GSM::get()->getNetwork().status;


@@ 568,19 546,36 @@ auto ServiceBluetooth::handle(cellular::NetworkStatusUpdateNotification *msg) ->
    sendWorkerCommand(std::make_unique<bluetooth::event::NetworkStatusData>(status));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallStartedNotification *msg) -> std::shared_ptr<sys::Message>

auto ServiceBluetooth::handle(cellular::CallerIdMessage *msg) -> std::shared_ptr<sys::Message>
{
    if (!msg->isCallIncoming()) {
        auto evt = std::make_unique<bluetooth::event::CallStarted>(msg->getNumber());
        sendWorkerCommand(std::move(evt));
    auto number = msg->number;
    auto btOn   = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::State));
    LOG_DEBUG("Received caller ID msg! ");

    if (btOn) {
        LOG_DEBUG("Sending to profile!");
        sendWorkerCommand(std::make_unique<bluetooth::event::IncomingCallNumber>(number));
    }

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::IncomingCallMessage *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(std::make_unique<bluetooth::event::StartRinging>());
    sendWorkerCommand(std::make_unique<bluetooth::event::IncomingCallStarted>());
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::CallStartedNotification *msg) -> std::shared_ptr<sys::Message>
{
    if (!msg->isCallIncoming()) {
        auto evt = std::make_unique<bluetooth::event::OutgoingCallStarted>(msg->getNumber());
        sendWorkerCommand(std::move(evt));
    }
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::CallOutgoingAccepted *msg) -> std::shared_ptr<sys::Message>
{
    LOG_DEBUG("Outgoing call accepted");


@@ 588,6 583,7 @@ auto ServiceBluetooth::handle(cellular::CallOutgoingAccepted *msg) -> std::share

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::CallActiveNotification *msg) -> std::shared_ptr<sys::Message>
{
    LOG_DEBUG("Incoming call accepted");


@@ 595,3 591,15 @@ auto ServiceBluetooth::handle(cellular::CallActiveNotification *msg) -> std::sha

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::CallEndedNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(std::make_unique<bluetooth::event::CallTerminated>());
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(cellular::CallMissedNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(std::make_unique<bluetooth::event::CallMissed>());
    return sys::MessageNone{};
}

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +5 -8
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 19,11 19,13 @@
#include <Timers/TimerHandle.hpp>

#include <memory> // for unique_ptr

namespace message::bluetooth
{
    class ResponseAuthenticate;
}
class BluetoothWorker;

namespace settings
{
    class Settings;


@@ 35,7 37,6 @@ namespace sdesktop
    namespace developerMode
    {
        class DeveloperModeRequest;
        class DeveloperModeMessageWrapper;
    } // namespace developerMode
} // namespace sdesktop



@@ 54,9 55,6 @@ namespace message::bluetooth
    class A2DPVolume;
    class HSPVolume;
    class HFPVolume;
    class Ring;
    class StartAudioRouting;
    class GetBluetoothDevicesModel;
    class RequestStatusIndicatorData;
} // namespace message::bluetooth



@@ 68,6 66,7 @@ namespace sevm
namespace cellular
{
    class CallEndedNotification;
    class CallMissedNotification;
    class CallStartedNotification;
    class CallOutgoingAccepted;
    class IncomingCallMessage;


@@ 80,7 79,6 @@ namespace cellular

class ServiceBluetooth : public sys::Service
{

  public:
    ServiceBluetooth();
    ~ServiceBluetooth();


@@ 120,7 118,6 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(message::bluetooth::SetStatus *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothPairResultMessage *msg) -> std::shared_ptr<sys::Message>;

    [[nodiscard]] auto handle(message::bluetooth::Unpair *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::RequestDeviceName *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::SetDeviceName *msg) -> std::shared_ptr<sys::Message>;


@@ 135,7 132,6 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(message::bluetooth::A2DPVolume *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::HSPVolume *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::HFPVolume *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::ResponseAuthenticatePin *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::ResponseAuthenticatePasskey *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::ResponseAuthenticatePairCancel *msg) -> std::shared_ptr<sys::Message>;


@@ 143,6 139,7 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(cellular::CallerIdMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::IncomingCallMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::CallEndedNotification *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::CallMissedNotification *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::CallStartedNotification *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::CallOutgoingAccepted *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(cellular::SignalStrengthUpdateNotification *msg) -> std::shared_ptr<sys::Message>;

D module-services/service-bluetooth/service-bluetooth/messages/AudioRouting.hpp => module-services/service-bluetooth/service-bluetooth/messages/AudioRouting.hpp +0 -12
@@ 1,12 0,0 @@
// 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

M module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp => module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp +11 -5
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 24,19 24,25 @@ namespace message::bluetooth
    class ConnectResult : public BluetoothMessage
    {
      public:
        explicit ConnectResult(Devicei device, bool succeed) : device(device), succeed(succeed)
        enum class Result
        {
            Success,
            Failure
        };

        explicit ConnectResult(Devicei device, Result result) : device(std::move(device)), result(result)
        {}
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return device;
        }
        [[nodiscard]] auto isSucceed() const noexcept -> bool
        [[nodiscard]] auto getResult() const noexcept -> Result
        {
            return succeed;
            return result;
        }

      private:
        Devicei device;
        bool succeed;
        Result result;
    };
} // namespace message::bluetooth

M module-services/service-cellular/QMBNManager.hpp => module-services/service-cellular/QMBNManager.hpp +3 -3
@@ 1,15 1,15 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Result.hpp"
#include "at/Result.hpp"
#include <string>

class DLCChannel;

namespace nv_paths
{

    auto constexpr voice_domain_pref = "/nv/item_files/modem/mmode/voice_domain_pref";
    auto constexpr sms_domain_pref   = "/nv/item_files/modem/mmode/sms_domain_pref";
    auto constexpr IMS_enable        = "/nv/item_files/ims/IMS_enable";

M pure_changelog.md => pure_changelog.md +1 -0
@@ 17,6 17,7 @@
* Fixed unsupported character in several quotes
* Fixed unwanted autolock on template selection window while rejecting call
* Fixed scenario when Alarm not being handled properly during a phone call
* Fixed handsfree device still ringing after caller has hung up

## [1.7.1 2023-07-13]