~aleteoryx/muditaos

2664e8ccec1540a59705cabfe0266b9b55fb2078 — Adam Dobrowolski 3 years ago e618939
[MOS-487] Move switch case to sm

massive switch case removed
added logger
moved state machine implementation
added double dispatch
44 files changed, 1479 insertions(+), 910 deletions(-)

M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp
A module-bluetooth/Bluetooth/AbstractController.hpp
A module-bluetooth/Bluetooth/BluetoothStateMachine.hpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.hpp
M module-bluetooth/Bluetooth/CommandHandler.cpp
M module-bluetooth/Bluetooth/CommandHandler.hpp
M module-bluetooth/Bluetooth/WorkerController.cpp
M module-bluetooth/Bluetooth/WorkerController.hpp
M module-bluetooth/Bluetooth/command/BatteryLevelData.hpp
M module-bluetooth/Bluetooth/command/Command.cpp
M module-bluetooth/Bluetooth/command/Command.hpp
M module-bluetooth/Bluetooth/command/CommandData.hpp
D module-bluetooth/Bluetooth/command/DeviceData.cpp
D module-bluetooth/Bluetooth/command/DeviceData.hpp
D module-bluetooth/Bluetooth/command/NetworkStatusData.cpp
D module-bluetooth/Bluetooth/command/NetworkStatusData.hpp
D module-bluetooth/Bluetooth/command/OperatorNameData.cpp
D module-bluetooth/Bluetooth/command/OperatorNameData.hpp
D module-bluetooth/Bluetooth/command/PhoneNumberData.cpp
D module-bluetooth/Bluetooth/command/PhoneNumberData.hpp
D module-bluetooth/Bluetooth/command/SignalStrengthData.cpp
D module-bluetooth/Bluetooth/command/SignalStrengthData.hpp
A module-bluetooth/Bluetooth/command/event/Events.hpp
A module-bluetooth/Bluetooth/doc/README.md
A module-bluetooth/Bluetooth/doc/uml/CMakeLists.txt
A module-bluetooth/Bluetooth/doc/uml/uml_printer.cpp
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/GAP/GAP.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bluetooth/CMakeLists.txt
M module-bluetooth/tests/CMakeLists.txt
M module-bluetooth/tests/tests-StatefulController.cpp
M module-bluetooth/tests/tests-command.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
A module-services/service-bluetooth/service-bluetooth/WorkerLock.hpp
M module-sys/Service/include/Service/Mailbox.hpp
M module-utils/log/Logger.cpp
M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp +1 -0
@@ 48,6 48,7 @@ void BluetoothSettingsModel::requestBondedDevices()

void BluetoothSettingsModel::requestScan()
{
    /// TODO send event Scan{}
    service->bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::Scan),
                             service::name::bluetooth);
}

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

#pragma once

namespace bt::evt
{
    struct Base;
    struct StartScan;
    struct StopScan;
    struct GetDevicesAvailable;
    struct VisibilityOn;
    struct VisibilityOff;
    struct ConnectAudio;
    struct DisconnectAudio;
    struct PowerOn;
    struct PowerOff;
    struct ShutDown;
    struct Pair;
    struct Unpair;
    struct StartRinging;
    struct StopRinging;
    struct StartRouting;
    struct StartStream;
    struct StopStream;
    struct CallAnswered;
    struct CallTerminated;
    struct CallStarted;
    struct IncomingCallNumber;
    struct SignalStrengthData;
    struct OperatorNameData;
    struct BatteryLevelData;
    struct NetworkStatusData;
} // namespace bt::evt

namespace bluetooth
{

    class AbstractController
    {
      public:
        virtual ~AbstractController() noexcept = default;

        virtual void handle(const bt::evt::Base &evt)                = 0;
        virtual void handle(const bt::evt::StartScan &evt)           = 0;
        virtual void handle(const bt::evt::StopScan &evt)            = 0;
        virtual void handle(const bt::evt::GetDevicesAvailable &evt) = 0;
        virtual void handle(const bt::evt::VisibilityOn &evt)        = 0;
        virtual void handle(const bt::evt::VisibilityOff &evt)       = 0;
        virtual void handle(const bt::evt::ConnectAudio &evt)        = 0;
        virtual void handle(const bt::evt::DisconnectAudio &evt)     = 0;
        virtual void handle(const bt::evt::PowerOn &evt)             = 0;
        virtual void handle(const bt::evt::PowerOff &evt)            = 0;
        virtual void handle(const bt::evt::ShutDown &evt)            = 0;
        virtual void handle(const bt::evt::Pair &evt)                = 0;
        virtual void handle(const bt::evt::Unpair &evt)              = 0;
        virtual void handle(const bt::evt::StartRinging &evt)        = 0;
        virtual void handle(const bt::evt::StopRinging &evt)         = 0;
        virtual void handle(const bt::evt::StartRouting &evt)        = 0;
        virtual void handle(const bt::evt::StartStream &evt)         = 0;
        virtual void handle(const bt::evt::StopStream &evt)          = 0;
        virtual void handle(const bt::evt::CallAnswered &evt)        = 0;
        virtual void handle(const bt::evt::CallTerminated &evt)      = 0;
        virtual void handle(const bt::evt::CallStarted &evt)         = 0;
        virtual void handle(const bt::evt::IncomingCallNumber &evt)  = 0;
        virtual void handle(const bt::evt::SignalStrengthData &evt)  = 0;
        virtual void handle(const bt::evt::OperatorNameData &evt)    = 0;
        virtual void handle(const bt::evt::BatteryLevelData &evt)    = 0;
        virtual void handle(const bt::evt::NetworkStatusData &evt)   = 0;
    };
}; // namespace bluetooth

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

#pragma once

#define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix
#include <boost/sml.hpp>
#include <sml-utils/Logger.hpp>
#include <stdexcept>
#include "command/event/Events.hpp"
#include "service-bluetooth/SettingsHolder.hpp"
#include "interface/profiles/ProfileManager.hpp"
#include "WorkerController.hpp"
#include "Device.hpp"
#include <log/log.hpp>

namespace bluetooth
{
    namespace sml = boost::sml;

    class InitializationError : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    class ProcessingError : public std::runtime_error
    {
      public:
        using std::runtime_error::runtime_error;
    };

    struct InitializationState
    {
        bool isInitDone = false;
    };

    struct InitDriver
    {
        void operator()(std::shared_ptr<AbstractDriver> driver)
        {
            if (driver == nullptr) {
                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) {
                throw InitializationError{"Unable to initialize a bluetooth driver."};
            }
        }
    } constexpr InitDriver;

    struct InitDevicesList
    {
        void operator()(const std::shared_ptr<bluetooth::SettingsHolder> settings,
                        const std::shared_ptr<std::vector<Devicei>> pairedDevices)
        {
            auto bondedDevicesStr =
                std::visit(bluetooth::StringVisitor(), settings->getValue(bluetooth::Settings::BondedDevices));
            pairedDevices->clear();
            auto devices = SettingsSerializer::fromString(bondedDevicesStr);
            pairedDevices->assign(devices.begin(), devices.end());
            LOG_INFO("Loaded: %d devices", int(pairedDevices->size()));
        }
    } constexpr InitDevicesList;

    struct IsInit
    {
        bool operator()(InitializationState &data)
        {
            return data.isInitDone;
        };
    } constexpr isInit;

    struct PostInit
    {
        // TODO shoundn't this be in driver?
        void operator()(DeviceRegistrationFunction registerDevice, InitializationState &data)
        {
            if (const auto status = registerDevice(); status != Error::Success) {
                throw InitializationError{"Unable to initialize bluetooth"};
            }
            data.isInitDone = true;
        }
    } constexpr PostInit;

    struct StartDriver
    {
        void operator()(std::shared_ptr<AbstractDriver> driver)
        {
            if (const auto status = driver->run(); status != Error::Success) {
                throw InitializationError{"Unable to run the bluetooth driver"};
            }
        }
    } static StartDriver;

    struct Setup
    {
      public:
        auto operator()() const
        {
            using namespace sml;
            return make_transition_table(*"Setup"_s + on_entry<_>[!isInit] / (InitDevicesList, InitDriver, PostInit),
                                         "Setup"_s / StartDriver = X);
        }
    };

    struct HandleOn
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)
        {
            handler->scan();
        }
    } constexpr HandleOn;

    struct HandleOff
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)
        {
            handler->stopScan();
        }
    } constexpr HandleOff;

    struct HandlePair
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler, const bt::evt::Pair &event)
        {
            handler->pair(event.device);
        }
    } constexpr HandlePair;

    struct Connected
    {
        bool operator()(std::shared_ptr<bluetooth::SettingsHolder> settings, bt::evt::Unpair evt)
        {
            auto deviceAddr =
                std::visit(bluetooth::StringVisitor(), settings->getValue(bluetooth::Settings::ConnectedDevice));
            return static_cast<bool>(deviceAddr == bd_addr_to_str(evt.device.address));
        }
    } constexpr Connected;

    struct HandleUnpair
    {
        void operator()(std::shared_ptr<AbstractDriver> driver, const bt::evt::Unpair &event)
        {
            driver->unpair(event.device);
        }
    } constexpr HandleUnpair;

    struct HandleDrop
    {
        void operator()(const bt::evt::Unpair &event,
                        std::shared_ptr<bluetooth::SettingsHolder> settings,
                        std::shared_ptr<std::vector<Devicei>> pairedDevices)
        {
            auto position = std::find_if(pairedDevices->begin(), pairedDevices->end(), [&](const Devicei &device) {
                return !bd_addr_cmp(event.device.address, device.address);
            });
            if (position != pairedDevices->end()) {
                pairedDevices->erase(position);
                settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(*pairedDevices));
                LOG_INFO("Device removed from paired devices list");
            }
        }
    } constexpr HandleDrop;

    struct HandleDisconnect
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)
        {
            handler->disconnectAudioConnection();
        }

    } constexpr HandleDisconnect;

    struct HandleSetVisibility
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)
        {
            handler->setVisibility(true);
        }

    } constexpr HandleSetVisibility;

    struct HandleUnsetVisibility
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler)
        {
            handler->setVisibility(false);
        }

    } constexpr HandleUnsetVisibility;

    struct EstablishAudioConnection
    {
        void operator()(std::shared_ptr<AbstractCommandHandler> handler, bt::evt::ConnectAudio evt)
        {
            handler->establishAudioConnection(evt.device);
        }

    } constexpr EstablishAudioConnection;

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

    struct StopRinging
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->stopRinging();
        }
    } constexpr StopRinging;

    struct InitializeCall
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->initializeCall();
        }
    } constexpr InitializeCall;

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

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

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

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

    struct SignalStrength
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager, bt::evt::SignalStrengthData evt)
        {
            profileManager->setSignalStrengthData(evt.strength);
        }
    } constexpr SignalStrength;

    struct SetOperatorName
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager, bt::evt::OperatorNameData evt)
        {
            profileManager->setOperatorNameData(bluetooth::OperatorName(evt.name));
        }
    } constexpr SetOperatorName;

    struct SetBatteryLevel
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager, bt::evt::BatteryLevelData evt)
        {
            profileManager->setBatteryLevelData(evt.level);
        }
    } constexpr SetBatteryLevel;

    struct SetNetworkStatus
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager, bt::evt::NetworkStatusData evt)
        {
            profileManager->setNetworkStatusData(evt.status);
        }
    } constexpr SetNetworkStatus;

    struct StartAudio
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->start();
        }
    } constexpr StartAudio;

    struct StopAudio
    {
        void operator()(std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->stop();
        }
    } constexpr StopAudio;

    struct On
    {
        auto operator()() const
        {
            auto isInit = [](InitializationState &data) { return data.isInitDone; };

            using namespace sml;
            // clang-format off
                return make_transition_table(
                       *"Idle"_s + event<bt::evt::StartScan>[isInit] / HandleOn = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopScan>[isInit] / HandleOff = "Idle"_s,
                       "Idle"_s + event<bt::evt::Pair>[isInit] / HandlePair = "Idle"_s,
                       "Idle"_s + event<bt::evt::Unpair>[isInit and Connected] / (HandleDisconnect, HandleUnpair) = "Idle"_s,
                       "Idle"_s + event<bt::evt::Unpair>[isInit] / (HandleUnpair, HandleDrop) = "Idle"_s,

                       "Idle"_s + event<bt::evt::VisibilityOn> / HandleSetVisibility = "Idle"_s,
                       "Idle"_s + event<bt::evt::VisibilityOff> / HandleUnsetVisibility = "Idle"_s,
                       "Idle"_s + event<bt::evt::ConnectAudio> / EstablishAudioConnection = "Idle"_s,

                       "Idle"_s + event<bt::evt::StartRinging> / StartRinging = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopRinging> / StopRinging = "Idle"_s,
                       "Idle"_s + event<bt::evt::StartRouting> /InitializeCall = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallAnswered> / CallAnswered = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallTerminated> / TerminateCall = "Idle"_s,
                       "Idle"_s + event<bt::evt::CallStarted>/ CallStarted = "Idle"_s,
                       "Idle"_s + event<bt::evt::IncomingCallNumber> / IncomingCall= "Idle"_s,
                       "Idle"_s + event<bt::evt::SignalStrengthData> / SignalStrength = "Idle"_s,
                       "Idle"_s + event<bt::evt::OperatorNameData>/  SetOperatorName = "Idle"_s,
                       "Idle"_s + event<bt::evt::BatteryLevelData>/ SetBatteryLevel = "Idle"_s,
                       "Idle"_s + event<bt::evt::NetworkStatusData> / SetNetworkStatus = "Idle"_s,
                       "Idle"_s + event<bt::evt::StartStream>/ StartAudio = "Idle"_s,
                       "Idle"_s + event<bt::evt::StopStream>/ StopAudio = "Idle"_s
                       );
            // clang-format on
        }
    };

    struct ExceptionHandler
    {
        void operator()(const std::runtime_error &err)
        {
            LOG_FATAL("EXCEPTION %s", err.what());
        }
    } constexpr ExceptionHandler;

    struct TurnOff
    {
        void operator()(std::shared_ptr<AbstractDriver> driver,
                        std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        {
            profileManager->deInit();
            driver->stop();
        };
    } constexpr TurnOff;

    class StateMachine
    {
      public:
        auto operator()() const
        {
            auto printInitError       = [](const InitializationError &error) { LOG_ERROR("%s", error.what()); };
            auto printProcessingError = [](const ProcessingError &error) { LOG_ERROR("%s", error.what()); };

            using namespace sml;
            // clang-format off
                return make_transition_table(*"Off"_s + event<bt::evt::PowerOn> = state<Setup>,
                                             state<Setup> = state<On>,
                                             state<Setup> + exception<InitializationError> / printInitError = "Off"_s,

                                             state<On> + event<bt::evt::PowerOff> / TurnOff = "Off"_s,
                                             state<On> + exception<ProcessingError> / ( printProcessingError, TurnOff ) = "Restart"_s,
                                             state<On> + event<bt::evt::ShutDown> / TurnOff = X,

                                             "Restart"_s = state<Setup>,
                                             "Restart"_s + event<bt::evt::ShutDown> /TurnOff = X,

                                            *("ExceptionsHandling"_s) + exception<std::runtime_error> / ExceptionHandler = "Off"_s,
                                            "ExceptionsHandling"_s + exception<std::runtime_error> / ExceptionHandler    = "Off"_s,

                                             "Off"_s + event<bt::evt::ShutDown> = X);
            // clang-format on
        }
    };

    class StatefulController::Impl
    {
      public:
        Impl() = delete;
        Impl(std::shared_ptr<AbstractDriver> driver,
             std::shared_ptr<AbstractCommandHandler> handler,
             DeviceRegistrationFunction registerDevice,
             std::shared_ptr<bluetooth::SettingsHolder> settings,
             std::shared_ptr<std::vector<Devicei>> pairedDevices,
             std::shared_ptr<bluetooth::BaseProfileManager> profileManager);
        using SM = sml::sm<StateMachine, boost::sml::logger<Logger>>;
        SM sm;
    };

    StatefulController::Impl::Impl(std::shared_ptr<AbstractDriver> driver,
                                   std::shared_ptr<AbstractCommandHandler> handler,
                                   DeviceRegistrationFunction registerDevice,
                                   std::shared_ptr<bluetooth::SettingsHolder> settings,
                                   std::shared_ptr<std::vector<Devicei>> pairedDevices,
                                   std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        : sm{Logger{}, driver, handler, registerDevice, InitializationState{}, settings, pairedDevices, profileManager}
    {}
} // namespace bluetooth

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +46 -74
@@ 10,7 10,6 @@
#include "interface/profiles/HSP/HSP.hpp"
#include "audio/BluetoothAudioDevice.hpp"
#include "BtKeysStorage.hpp"
#include "command/DeviceData.hpp"

#if DEBUG_BLUETOOTH_HCI_COMS == 1
#define logHciComs(...) LOG_DEBUG(__VA_ARGS__)


@@ 71,32 70,44 @@ namespace
    };

    auto createStatefulController(sys::Service *service,
                                  bluetooth::RunLoop *loop,
                                  std::shared_ptr<bluetooth::SettingsHolder> settings,
                                  std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                  DeviceRegistration::OnLinkKeyAddedCallback &&onLinkKeyAddedCallback)
                                  std::shared_ptr<bluetooth::Driver> driver,
                                  std::shared_ptr<bluetooth::CommandHandler> commandHandler,
                                  const std::shared_ptr<bluetooth::SettingsHolder> &settings,
                                  std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                                  DeviceRegistration::OnLinkKeyAddedCallback &&onLinkKeyAddedCallback,
                                  std::shared_ptr<std::vector<Devicei>> pairedDevices)
    {
        auto driver = std::make_shared<bluetooth::Driver>(loop->getRunLoopInstance(), service);
        auto commandHandler =
            std::make_unique<bluetooth::CommandHandler>(service, settings, std::move(profileManager), driver);
        ;

        return std::make_unique<bluetooth::StatefulController>(
            std::move(driver),
            std::move(commandHandler),
            DeviceRegistration{std::move(settings), std::move(onLinkKeyAddedCallback)});
            driver,
            commandHandler,
            DeviceRegistration{settings, std::move(onLinkKeyAddedCallback)},
            settings,
            pairedDevices,
            profileManager);
    }
} // namespace

BluetoothWorker::BluetoothWorker(sys::Service *service)
    : Worker(service, BluetoothWorkerStackDepth), service(service),
      profileManager(std::make_shared<bluetooth::ProfileManager>(service)),
      settings(static_cast<ServiceBluetooth *>(service)->settingsHolder),
      settings(dynamic_cast<ServiceBluetooth *>(service)->settingsHolder),
      runLoop(std::make_unique<bluetooth::RunLoop>()),
      driver(std::make_shared<bluetooth::Driver>(runLoop->getRunLoopInstance(), service)),
      handler(std::make_shared<bluetooth::CommandHandler>(service, settings, profileManager, driver)),
      controller{createStatefulController(
          service, runLoop.get(), settings, profileManager, [this](const std::string &addr) { onLinkKeyAdded(addr); })}
          service,
          driver,
          handler,
          settings,
          profileManager,
          [this](const std::string &addr) { onLinkKeyAdded(addr); },
          pairedDevices)}
{
    init({
        {queues::io, sizeof(bluetooth::Message), queues::queueLength},
        {queues::cmd, sizeof(bluetooth::Command::CommandPack), queues::queueLength},
        {queues::cmd, 0, queues::queueLength},
        {queues::btstack, sizeof(bool), queues::triggerQueueLength},
    });
    registerQueues();


@@ 104,7 115,10 @@ BluetoothWorker::BluetoothWorker(sys::Service *service)

void BluetoothWorker::registerQueues()
{
    static_cast<ServiceBluetooth *>(service)->workerQueue = Worker::getQueueHandleByName(queues::cmd);
    workerQueue = std::make_shared<Mailbox<bluetooth::Command, QueueHandle_t, WorkerLock>>(
        Worker::getQueueHandleByName(queues::cmd));

    dynamic_cast<ServiceBluetooth *>(service)->workerQueue = workerQueue;
    runLoop->setTriggerQueue(Worker::getQueueHandleByName(queues::btstack));
    bsp::BlueKitchen::getInstance()->qHandle = queues[queueIO_handle]->GetQueueHandle();
}


@@ 114,15 128,15 @@ void BluetoothWorker::onLinkKeyAdded(const std::string &deviceAddress)
    auto devices = bluetooth::GAP::getDevicesList();
    for (auto &device : devices) {
        if (bd_addr_to_str(device.address) == deviceAddress) {
            pairedDevices.emplace_back(device);
            settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(pairedDevices));
            pairedDevices->emplace_back(device);
            settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(*pairedDevices));
        }
    }
}

BluetoothWorker::~BluetoothWorker()
{
    controller->shutdown();
    controller->handle(bt::evt::ShutDown{});
}

auto BluetoothWorker::run() -> bool


@@ 141,33 155,18 @@ auto BluetoothWorker::run() -> bool

auto BluetoothWorker::handleCommand(QueueHandle_t queue) -> bool
{
    bluetooth::Command::CommandPack pack{};
    if (xQueueReceive(queue, static_cast<void *>(&pack), 0) != pdTRUE) {
        LOG_ERROR("Queue receive failure!");
        return false;
    }
    auto command = bluetooth::Command(std::move(pack));

    switch (command.getType()) {
    case bluetooth::Command::PowerOn:
        initDevicesList();
        controller->turnOn();
        break;
    case bluetooth::Command::Unpair: {
        controller->processCommand(command);
        auto device = std::get<Devicei>(command.getData());
        removeFromBoundDevices(device.address);
        handleUnpairDisconnect(device);
    } break;
    case bluetooth::Command::None:
        break;
    case bluetooth::Command::PowerOff:
        controller->processCommand(command);
        controller->turnOff();
        break;
    default:
        controller->processCommand(command);
        break;
    LOG_INFO("handle bluetooth command(s)");
    xQueueReceive(queue, nullptr, 0);
    while (not workerQueue->empty()) {
        auto cmd = workerQueue->peek();
        if (cmd == std::nullopt) {
            LOG_ERROR("There was no data even with notification");
            break;
        }
        if ((*cmd).evt != nullptr) {
            controller->handle(*cmd->evt);
            delete cmd->evt;
        }
    }
    return true;
}


@@ 264,24 263,12 @@ auto BluetoothWorker::handleMessage(uint32_t queueID) -> bool

void BluetoothWorker::closeWorker()
{
    controller->turnOff();
    controller->handle(bt::evt::PowerOff{});
    this->close();
}
void BluetoothWorker::initDevicesList()
{
    auto bondedDevicesStr =
        std::visit(bluetooth::StringVisitor(), settings->getValue(bluetooth::Settings::BondedDevices));
    pairedDevices = SettingsSerializer::fromString(bondedDevicesStr);
}

void BluetoothWorker::removeFromBoundDevices(uint8_t *addr)
{
    auto position = std::find_if(
        pairedDevices.begin(), pairedDevices.end(), [&](Devicei device) { return !bd_addr_cmp(addr, device.address); });
    if (position != pairedDevices.end()) {
        pairedDevices.erase(position);
        settings->setValue(bluetooth::Settings::BondedDevices, SettingsSerializer::toString(pairedDevices));
        LOG_INFO("Device removed from paired devices list");
    }
}

void BluetoothWorker::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> device)


@@ 289,18 276,3 @@ void BluetoothWorker::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDe
    cpp_freertos::LockGuard lock(loopMutex);
    profileManager->setAudioDevice(std::move(device));
}
auto BluetoothWorker::isAddressConnected(const uint8_t *addr) -> bool
{
    auto deviceAddr =
        std::visit(bluetooth::StringVisitor(), this->settings->getValue(bluetooth::Settings::ConnectedDevice));
    return static_cast<bool>(deviceAddr == bd_addr_to_str(addr));
}
void BluetoothWorker::handleUnpairDisconnect(const Devicei &device)
{
    if (isAddressConnected(device.address)) {
        auto commandData   = std::make_unique<bluetooth::DeviceData>(device);
        auto disconnectCmd = bluetooth::Command(
            bluetooth::Command::CommandPack{bluetooth::Command::DisconnectAudio, std::move(commandData)});
        controller->processCommand(disconnectCmd);
    }
}

M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +16 -5
@@ 7,6 7,7 @@
#include "glucode/BluetoothRunLoop.hpp"
#include "interface/profiles/Profile.hpp"
#include "service-bluetooth/SettingsHolder.hpp"
#include "service-bluetooth/WorkerLock.hpp"
#include "Service/Worker.hpp"

#include "Device.hpp"


@@ 61,6 62,15 @@ namespace bluetooth
    };
}; // namespace bluetooth

struct DeviceStore
{};

namespace bluetooth
{
    class Driver;
    class CommandHandler;
}; // namespace bluetooth

class BluetoothWorker : private sys::Worker
{
    enum WorkerEventQueues


@@ 78,10 88,9 @@ class BluetoothWorker : private sys::Worker

    void registerQueues();
    void onLinkKeyAdded(const std::string &deviceAddress);
    void initDevicesList();
    void removeFromBoundDevices(uint8_t *addr);
    auto isAddressConnected(const bd_addr_t addr) -> bool;
    void handleUnpairDisconnect(const Devicei &device);

    std::shared_ptr<Mailbox<bluetooth::Command, QueueHandle_t, WorkerLock>> workerQueue;

  public:
    enum Error


@@ 91,7 100,7 @@ class BluetoothWorker : private sys::Worker
        ErrorBtAPI,
    };

    BluetoothWorker(sys::Service *service);
    explicit BluetoothWorker(sys::Service *service);
    ~BluetoothWorker() override;

    auto handleMessage(uint32_t queueID) -> bool override;


@@ 107,7 116,9 @@ class BluetoothWorker : private sys::Worker
    unsigned long active_features;
    std::shared_ptr<bluetooth::ProfileManager> profileManager;
    std::shared_ptr<bluetooth::SettingsHolder> settings;
    std::vector<Devicei> pairedDevices;
    std::shared_ptr<std::vector<Devicei>> pairedDevices = std::make_shared<std::vector<Devicei>>();
    std::unique_ptr<bluetooth::RunLoop> runLoop;
    std::shared_ptr<bluetooth::Driver> driver;
    std::shared_ptr<bluetooth::CommandHandler> handler;
    std::unique_ptr<bluetooth::AbstractController> controller;
};

M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +5 -63
@@ 31,7 31,7 @@ namespace bluetooth

    CommandHandler::CommandHandler(sys::Service *service,
                                   std::shared_ptr<bluetooth::SettingsHolder> settings,
                                   std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                   std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                                   std::shared_ptr<bluetooth::AbstractDriver> driver)
        : service{service}, settings{std::move(settings)}, profileManager{std::move(profileManager)}, driver{std::move(
                                                                                                          driver)}


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

    Error::Code CommandHandler::handle(Command &command)
    {
        switch (command.getType()) {
        case bluetooth::Command::PowerOn:
            return Error::Success;
        case bluetooth::Command::StartScan:
            return scan();
        case bluetooth::Command::getDevicesAvailable:
            return availableDevices();
        case bluetooth::Command::StopScan:
            return stopScan();
        case bluetooth::Command::Pair:
            return pair(command.getData());
        case bluetooth::Command::Unpair:
            return unpair(command.getData());
        case bluetooth::Command::VisibilityOn:
            return setVisibility(true);
        case bluetooth::Command::VisibilityOff:
            return setVisibility(false);
        case bluetooth::Command::ConnectAudio:
            return establishAudioConnection(command.getData());
        case bluetooth::Command::DisconnectAudio:
            return disconnectAudioConnection();
        case bluetooth::Command::PowerOff:
            profileManager->deInit();
            return Error::Success;
        case bluetooth::Command::None:
            return Error::Success;
        case Command::StartRinging:
            return profileManager->startRinging();
        case Command::StopRinging:
            return profileManager->stopRinging();
        case Command::StartRouting:
            return profileManager->initializeCall();
        case Command::CallAnswered:
            return profileManager->callAnswered();
        case Command::CallTerminated:
            return profileManager->terminateCall();
        case Command::CallStarted:
            return profileManager->callStarted(command.getData());
        case Command::IncomingCallNumber:
            return profileManager->setIncomingCallNumber(command.getData());
        case Command::SignalStrengthData:
            return profileManager->setSignalStrengthData(command.getData());
        case Command::OperatorNameData:
            return profileManager->setOperatorNameData(command.getData());
        case Command::BatteryLevelData:
            return profileManager->setBatteryLevelData(command.getData());
        case Command::NetworkStatusData:
            return profileManager->setNetworkStatusData(command.getData());
        case Command::StartStream:
            profileManager->start();
            return Error::Success;
        case Command::StopStream:
            profileManager->stop();
            return Error::Success;
        }
        return Error::LibraryError;
    }

    Error::Code CommandHandler::scan()
    {



@@ 144,7 84,8 @@ namespace bluetooth
    {
        auto device = std::get<Devicei>(data);
        LOG_INFO("Pairing...");
        const auto errorCode = driver->pair(device) ? Error::Success : Error::LibraryError;
        auto errorCode = Error::Code::Success;
        driver->pair(device);
        LOG_INFO("Pairing result: %s", magic_enum::enum_name(errorCode).data());
        return errorCode;
    }


@@ 152,7 93,8 @@ namespace bluetooth
    {
        auto device = std::get<Devicei>(data);
        LOG_INFO("Unpairing...");
        const auto errorCode = driver->unpair(device) ? Error::Success : Error::LibraryError;
        const auto errorCode = Error::Code::Success;
        driver->unpair(device);
        LOG_INFO("Unpairing result: %s", magic_enum::enum_name(errorCode).data());
        return errorCode;
    }

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +22 -12
@@ 27,7 27,14 @@ namespace bluetooth
      public:
        virtual ~AbstractCommandHandler() noexcept = default;

        virtual auto handle(Command &command) -> Error::Code = 0;
        virtual Error::Code scan()                                            = 0;
        virtual Error::Code stopScan()                                        = 0;
        virtual Error::Code setVisibility(bool visibility)                    = 0;
        virtual Error::Code establishAudioConnection(const DataVariant &data) = 0;
        virtual Error::Code disconnectAudioConnection()                       = 0;
        virtual Error::Code pair(const DataVariant &data)                     = 0;
        virtual Error::Code unpair(const DataVariant &data)                   = 0;
        virtual Error::Code availableDevices()                                = 0;
    };

    class CommandHandler : public AbstractCommandHandler


@@ 35,23 42,26 @@ namespace bluetooth
      public:
        explicit CommandHandler(sys::Service *service,
                                std::shared_ptr<bluetooth::SettingsHolder> settings,
                                std::shared_ptr<bluetooth::ProfileManager> profileManager,
                                std::shared_ptr<bluetooth::BaseProfileManager> profileManager,
                                std::shared_ptr<bluetooth::AbstractDriver> driver);

        auto handle(Command &command) -> Error::Code override;
        Error::Code scan() override;
        Error::Code stopScan() override;
        Error::Code setVisibility(bool visibility) override;
        Error::Code establishAudioConnection(const DataVariant &data) override;
        Error::Code disconnectAudioConnection() override;
        Error::Code pair(const DataVariant &data) override;
        Error::Code unpair(const DataVariant &data) override;
        Error::Code availableDevices() override;

      private:
        Error::Code scan();
        Error::Code stopScan();
        Error::Code setVisibility(bool visibility);
        Error::Code establishAudioConnection(const DataVariant &data);
        Error::Code disconnectAudioConnection();
        Error::Code pair(const DataVariant &data);
        Error::Code unpair(const DataVariant &data);
        Error::Code availableDevices();
        sys::Service *service;
        std::shared_ptr<bluetooth::SettingsHolder> settings;
        std::shared_ptr<bluetooth::ProfileManager> profileManager;

      public:
        std::shared_ptr<bluetooth::BaseProfileManager> profileManager;

      private:
        std::shared_ptr<AbstractDriver> driver;
    };
} // namespace bluetooth

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

#include "WorkerController.hpp"

#include "Device.hpp"
#include "interface/profiles/ProfileManager.hpp"

#include <log/log.hpp>
#define BOOST_SML_CFG_DISABLE_MIN_SIZE // GCC10 fix
#include <boost/sml.hpp>
#include <magic_enum.hpp>
#include <stdexcept>
#include "BluetoothStateMachine.hpp"

namespace bluetooth
{
    namespace sml = boost::sml;

    namespace
    {
        struct TurnOn
        {};
        struct TurnOff
        {};
        struct ShutDown
        {};
        struct ProcessCommand
        {
            Command &command;
        };

        class InitializationError : public std::runtime_error
        {
          public:
            using std::runtime_error::runtime_error;
        };

        class ProcessingError : public std::runtime_error
        {
          public:
            using std::runtime_error::runtime_error;
        };

        struct InitializationState
        {
            bool isInitDone = false;
        };

        struct Setup
        {
          public:
            auto operator()() const
            {
                auto isInit = [](InitializationState &data) { return data.isInitDone; };
                auto init   = [](std::shared_ptr<AbstractDriver> &driver) {
                    if (const auto status = driver->init(); status != Error::Success) {
                        throw InitializationError{"Unable to initialize a bluetooth driver."};
                    }
                };
                auto setup = [](DeviceRegistrationFunction &registerDevice, InitializationState &data) {
                    if (const auto status = registerDevice(); status != Error::Success) {
                        throw InitializationError{"Unable to initialize bluetooth"};
                    }
                    data.isInitDone = true;
                };
                auto startDriver = [](std::shared_ptr<AbstractDriver> &driver) {
                    if (const auto status = driver->run(); status != Error::Success) {
                        throw InitializationError{"Unable to run the bluetooth driver"};
                    }
                };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Setup"_s / startDriver = "StartingDriver"_s,
                                             "Setup"_s + on_entry<_> [ !isInit ] / ( init, setup ),
                                             "StartingDriver"_s = X);
                // clang-format on
            }
        };

        struct On
        {
            auto operator()() const
            {
                auto isInit        = [](InitializationState &data) { return data.isInitDone; };
                auto handleCommand = [](std::shared_ptr<AbstractCommandHandler> &processor,
                                        const ProcessCommand &processCommand) {
                    if (const auto status = processor->handle(processCommand.command); status != Error::Success) {
                        throw ProcessingError{"Failed to process command"};
                    }
                };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Idle"_s + event<ProcessCommand> [ isInit ] / handleCommand = "Processing"_s,
                                             "Processing"_s = "Idle"_s);
                // clang-format on
            }
        };

        class StateMachine
        {
          public:
            auto operator()() const
            {
                auto turnOff              = [](std::shared_ptr<AbstractDriver> &driver) { driver->stop(); };
                auto printInitError       = [](const InitializationError &error) { LOG_ERROR("%s", error.what()); };
                auto printProcessingError = [](const ProcessingError &error) { LOG_ERROR("%s", error.what()); };

                using namespace sml;
                // clang-format off
                return make_transition_table(*"Off"_s + event<TurnOn> = state<Setup>,
                                             state<Setup> = state<On>,
                                             state<Setup> + exception<InitializationError> / printInitError = "Off"_s,
                                             state<On> + event<TurnOff> / turnOff = "Off"_s,
                                             state<On> + exception<ProcessingError> / ( printProcessingError, turnOff ) = "Restart"_s,
                                             "Restart"_s = state<Setup>,
                                             "Off"_s + event<ShutDown> = X);
                // clang-format on
            }
        };
    } // namespace

    class StatefulController::Impl
    {
      public:
        Impl(std::shared_ptr<AbstractDriver> &&driver,
             std::shared_ptr<AbstractCommandHandler> &&handler,
             DeviceRegistrationFunction &&registerDevice);

        using SM = sml::sm<StateMachine>;
        SM sm;
    };

    StatefulController::Impl::Impl(std::shared_ptr<AbstractDriver> &&driver,
                                   std::shared_ptr<AbstractCommandHandler> &&handler,
                                   DeviceRegistrationFunction &&registerDevice)
        : sm{std::move(driver), std::move(handler), std::move(registerDevice), InitializationState{}}
    {}

    StatefulController::StatefulController(std::shared_ptr<AbstractDriver> &&driver,
                                           std::shared_ptr<AbstractCommandHandler> &&handler,
                                           DeviceRegistrationFunction &&registerDevice)
        : pimpl(std::make_unique<Impl>(std::move(driver), std::move(handler), std::move(registerDevice)))
    {}

    StatefulController::~StatefulController() noexcept = default;

    void StatefulController::turnOn()
    StatefulController::StatefulController(std::shared_ptr<AbstractDriver> driver,
                                           std::shared_ptr<AbstractCommandHandler> handler,
                                           DeviceRegistrationFunction registerDevice,
                                           std::shared_ptr<bluetooth::SettingsHolder> settings,
                                           std::shared_ptr<std::vector<Devicei>> pairedDevices,
                                           std::shared_ptr<bluetooth::BaseProfileManager> profileManager)
        : pimpl(std::make_unique<Impl>(driver, handler, registerDevice, settings, pairedDevices, profileManager))
    {
        pimpl->sm.process_event(TurnOn{});
    }

    void StatefulController::turnOff()
    StatefulController::StatefulController(StatefulController &&other) noexcept
    {
        pimpl->sm.process_event(TurnOff{});
        this->pimpl = std::move(other.pimpl);
    }

    void StatefulController::shutdown()
    StatefulController &StatefulController::operator=(StatefulController &&other) noexcept
    {
        if (isOn()) {
            turnOff();
        }
        pimpl->sm.process_event(ShutDown{});
        this->pimpl = std::move(other.pimpl);
        return *this;
    }

    auto StatefulController::isOn() const -> bool
    {
        using namespace sml;
        return !pimpl->sm.is("Off"_s) && !isTerminated();
    }
    StatefulController::~StatefulController() noexcept = default;

    auto StatefulController::isTerminated() const -> bool
    //--------------------------------------------------------------------
    // entry to dispatch to call handle (double visitor -> double dispatch)
    void StatefulController::handle(const bt::evt::Base &evt)
    {
        using namespace sml;
        return pimpl->sm.is(X);
        evt.dispatch(this);
    }

    void StatefulController::processCommand(Command &command)
    {
        try {
            LOG_INFO("Process command: %s", magic_enum::enum_name(command.getType()).data());
            pimpl->sm.process_event(ProcessCommand{command});
            LOG_DEBUG("Command processed");
        }
        catch (std::bad_variant_access &e) {
            LOG_ERROR(
                "Failed to parse command %s, error: %s", magic_enum::enum_name(command.getType()).data(), e.what());
        }
    }
    //-----------------------------------------
    // all `handle` code below is casual visitor

    void StatefulController::handle(const bt::evt::StartScan &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StopScan &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::GetDevicesAvailable &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::VisibilityOn &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::VisibilityOff &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::ConnectAudio &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::DisconnectAudio &evt)
    {
        pimpl->sm.process_event(evt);
    };
    // TODO split TurnOn and PowerOn?
    void StatefulController::handle(const bt::evt::PowerOn &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::PowerOff &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::ShutDown &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::Pair &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::Unpair &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StartRinging &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StopRinging &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StartRouting &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StartStream &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::StopStream &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::CallAnswered &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::CallTerminated &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::CallStarted &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::IncomingCallNumber &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::SignalStrengthData &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::OperatorNameData &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::BatteryLevelData &evt)
    {
        pimpl->sm.process_event(evt);
    };
    void StatefulController::handle(const bt::evt::NetworkStatusData &evt)
    {
        pimpl->sm.process_event(evt);
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/WorkerController.hpp => module-bluetooth/Bluetooth/WorkerController.hpp +41 -24
@@ 3,8 3,10 @@

#pragma once

#include "AbstractController.hpp"
#include "CommandHandler.hpp"
#include "interface/BluetoothDriver.hpp"
#include <command/event/Events.hpp>

#include <functional>
#include <memory>


@@ 13,38 15,53 @@ namespace bluetooth
{
    using DeviceRegistrationFunction = std::function<Error::Code()>;

    class AbstractController
    {
      public:
        virtual ~AbstractController() noexcept = default;

        virtual void turnOn()                     = 0;
        virtual void turnOff()                    = 0;
        virtual void shutdown()                   = 0;
        virtual auto isOn() const -> bool         = 0;
        virtual auto isTerminated() const -> bool = 0;

        virtual void processCommand(Command &command) = 0;
    };

    class StatefulController : public AbstractController
    {
      public:
        StatefulController(std::shared_ptr<AbstractDriver> &&driver,
                           std::shared_ptr<AbstractCommandHandler> &&handler,
                           DeviceRegistrationFunction &&registerDevice);
        StatefulController(std::shared_ptr<AbstractDriver> driver,
                           std::shared_ptr<AbstractCommandHandler> handler,
                           DeviceRegistrationFunction registerDevice,
                           std::shared_ptr<bluetooth::SettingsHolder> settings,
                           std::shared_ptr<std::vector<Devicei>> pairedDevices,
                           std::shared_ptr<bluetooth::BaseProfileManager> profileManager);
        StatefulController()                                = delete;
        StatefulController(const StatefulController &other) = delete;
        StatefulController(StatefulController &&other) noexcept;
        StatefulController &operator=(const StatefulController &other) = delete;
        StatefulController &operator                                   =(StatefulController &&other) noexcept;

        ~StatefulController() noexcept override;

        void turnOn() override;
        void turnOff() override;
        void shutdown() override;
        [[nodiscard]] auto isOn() const -> bool override;
        [[nodiscard]] auto isTerminated() const -> bool override;
        void handle(const bt::evt::Base &evt) override;
        void handle(const bt::evt::StartScan &evt) override;
        void handle(const bt::evt::StopScan &evt) override;
        void handle(const bt::evt::GetDevicesAvailable &evt) override;
        void handle(const bt::evt::VisibilityOn &evt) override;
        void handle(const bt::evt::VisibilityOff &evt) override;
        void handle(const bt::evt::ConnectAudio &evt) override;
        void handle(const bt::evt::DisconnectAudio &evt) override;
        void handle(const bt::evt::PowerOn &evt) override;
        void handle(const bt::evt::PowerOff &evt) override;
        void handle(const bt::evt::ShutDown &evt) override;
        void handle(const bt::evt::Pair &evt) override;
        void handle(const bt::evt::Unpair &evt) override;
        void handle(const bt::evt::StartRinging &evt) override;
        void handle(const bt::evt::StopRinging &evt) override;
        void handle(const bt::evt::StartRouting &evt) override;
        void handle(const bt::evt::StartStream &evt) override;
        void handle(const bt::evt::StopStream &evt) override;
        void handle(const bt::evt::CallAnswered &evt) override;
        void handle(const bt::evt::CallTerminated &evt) override;
        void handle(const bt::evt::CallStarted &evt) override;
        void handle(const bt::evt::IncomingCallNumber &evt) override;
        void handle(const bt::evt::SignalStrengthData &evt) override;
        void handle(const bt::evt::OperatorNameData &evt) override;
        void handle(const bt::evt::BatteryLevelData &evt) override;
        void handle(const bt::evt::NetworkStatusData &evt) override;

        void processCommand(Command &command) override;
        class Impl;

      private:
        class Impl;
        std::unique_ptr<Impl> pimpl;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/command/BatteryLevelData.hpp => module-bluetooth/Bluetooth/command/BatteryLevelData.hpp +1 -1
@@ 7,7 7,7 @@
namespace bluetooth
{

    class BatteryLevelData : public CommandData
    class BatteryLevelData : public Action
    {
      public:
        explicit BatteryLevelData(const BatteryLevel &level);

M module-bluetooth/Bluetooth/command/Command.cpp => module-bluetooth/Bluetooth/command/Command.cpp +0 -17
@@ 6,21 6,4 @@

namespace bluetooth
{

    Command::Command(CommandPack &&pack) : pack(std::move(pack))
    {}

    auto Command::getType() const noexcept -> Command::Type
    {
        return pack.commandType;
    }

    auto Command::getData() -> DataVariant
    {
        if (pack.data == nullptr) {
            LOG_ERROR("Terrible,terrible damage!");
            return DataVariant{};
        }
        return pack.data->getData();
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/command/Command.hpp => module-bluetooth/Bluetooth/command/Command.hpp +3 -47
@@ 3,58 3,14 @@

#pragma once
#include "CommandData.hpp"
#include "event/Events.hpp"

namespace bluetooth
{

    class Command
    struct Command
    {
      public:
        enum Type
        {
            StartScan,
            StopScan,
            getDevicesAvailable,
            VisibilityOn,
            VisibilityOff,
            ConnectAudio,
            DisconnectAudio,
            PowerOn,
            PowerOff,
            Pair,
            Unpair,
            StartRinging,
            StopRinging,
            StartRouting,
            StartStream,
            StopStream,
            CallAnswered,
            CallTerminated,
            CallStarted,
            IncomingCallNumber,
            SignalStrengthData,
            OperatorNameData,
            BatteryLevelData,
            NetworkStatusData,
            None,
        };

        struct CommandPack
        {
            Command::Type commandType         = Command::None;
            std::unique_ptr<CommandData> data = nullptr;
        };

        explicit Command(CommandPack &&);
        explicit Command(Command::Type type)
        {
            pack.commandType = type;
        }
        auto getType() const noexcept -> Command::Type;
        auto getData() -> DataVariant;

      private:
        CommandPack pack;
        bt::evt::Base *evt = nullptr;
    };

} // namespace bluetooth

M module-bluetooth/Bluetooth/command/CommandData.hpp => module-bluetooth/Bluetooth/command/CommandData.hpp +0 -7
@@ 18,11 18,4 @@ namespace bluetooth
                                     Devicei,
                                     utils::PhoneNumber::View,
                                     Store::Network::Status>;

    class CommandData
    {
      public:
        virtual auto getData() -> DataVariant = 0;
        virtual ~CommandData()                = default;
    };
} // namespace bluetooth

D module-bluetooth/Bluetooth/command/DeviceData.cpp => module-bluetooth/Bluetooth/command/DeviceData.cpp +0 -16
@@ 1,16 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DeviceData.hpp"

namespace bluetooth
{
    DeviceData::DeviceData(const Devicei &device) : device(device)
    {}

    auto DeviceData::getData() -> DataVariant
    {
        return device;
    }

} // namespace bluetooth

D module-bluetooth/Bluetooth/command/DeviceData.hpp => module-bluetooth/Bluetooth/command/DeviceData.hpp +0 -21
@@ 1,21 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CommandData.hpp"
#include "Device.hpp"

namespace bluetooth
{

    class DeviceData : public CommandData
    {
      public:
        explicit DeviceData(const Devicei &device);
        auto getData() -> DataVariant override;

      private:
        Devicei device;
    };

} // namespace bluetooth

D module-bluetooth/Bluetooth/command/NetworkStatusData.cpp => module-bluetooth/Bluetooth/command/NetworkStatusData.cpp +0 -14
@@ 1,14 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NetworkStatusData.hpp"

namespace bluetooth
{
    NetworkStatusData::NetworkStatusData(const Store::Network::Status &status) : status(status)
    {}
    auto NetworkStatusData::getData() -> DataVariant
    {
        return status;
    }
} // namespace bluetooth

D module-bluetooth/Bluetooth/command/NetworkStatusData.hpp => module-bluetooth/Bluetooth/command/NetworkStatusData.hpp +0 -20
@@ 1,20 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CommandData.hpp"

namespace bluetooth
{

    class NetworkStatusData : public CommandData
    {
      public:
        explicit NetworkStatusData(const Store::Network::Status &status);
        auto getData() -> DataVariant override;

      private:
        Store::Network::Status status;
    };

} // namespace bluetooth

D module-bluetooth/Bluetooth/command/OperatorNameData.cpp => module-bluetooth/Bluetooth/command/OperatorNameData.cpp +0 -16
@@ 1,16 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "OperatorNameData.hpp"

#include <utility>

namespace bluetooth
{
    OperatorNameData::OperatorNameData(OperatorName operatorName) : operatorName(std::move(operatorName))
    {}
    auto OperatorNameData::getData() -> DataVariant
    {
        return operatorName;
    }
} // namespace bluetooth

D module-bluetooth/Bluetooth/command/OperatorNameData.hpp => module-bluetooth/Bluetooth/command/OperatorNameData.hpp +0 -20
@@ 1,20 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CommandData.hpp"

namespace bluetooth
{

    class OperatorNameData : public CommandData
    {
      public:
        explicit OperatorNameData(OperatorName operatorName);
        auto getData() -> DataVariant override;

      private:
        OperatorName operatorName;
    };

} // namespace bluetooth

D module-bluetooth/Bluetooth/command/PhoneNumberData.cpp => module-bluetooth/Bluetooth/command/PhoneNumberData.cpp +0 -16
@@ 1,16 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "PhoneNumberData.hpp"

namespace bluetooth
{
    PhoneNumberData::PhoneNumberData(const utils::PhoneNumber::View &view) : view(view)
    {}
    PhoneNumberData::PhoneNumberData(const utils::PhoneNumber &number) : view(number.getView())
    {}
    auto PhoneNumberData::getData() -> DataVariant
    {
        return view;
    }
} // namespace bluetooth

D module-bluetooth/Bluetooth/command/PhoneNumberData.hpp => module-bluetooth/Bluetooth/command/PhoneNumberData.hpp +0 -21
@@ 1,21 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CommandData.hpp"

namespace bluetooth
{

    class PhoneNumberData : public CommandData
    {
      public:
        explicit PhoneNumberData(const utils::PhoneNumber::View &view);
        explicit PhoneNumberData(const utils::PhoneNumber &number);
        auto getData() -> DataVariant override;

      private:
        utils::PhoneNumber::View view;
    };

} // namespace bluetooth

D module-bluetooth/Bluetooth/command/SignalStrengthData.cpp => module-bluetooth/Bluetooth/command/SignalStrengthData.cpp +0 -14
@@ 1,14 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SignalStrengthData.hpp"

namespace bluetooth
{
    SignalStrengthData::SignalStrengthData(const Store::SignalStrength &signalStrength) : signalStrength(signalStrength)
    {}
    auto SignalStrengthData::getData() -> DataVariant
    {
        return signalStrength;
    }
} // namespace bluetooth

D module-bluetooth/Bluetooth/command/SignalStrengthData.hpp => module-bluetooth/Bluetooth/command/SignalStrengthData.hpp +0 -20
@@ 1,20 0,0 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CommandData.hpp"

namespace bluetooth
{

    class SignalStrengthData : public CommandData
    {
      public:
        explicit SignalStrengthData(const Store::SignalStrength &signalStrength);
        auto getData() -> DataVariant override;

      private:
        Store::SignalStrength signalStrength;
    };

} // namespace bluetooth

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

#pragma once

#include "Device.hpp"
#include "EventStore.hpp"
#include "module-utils/phonenumber/PhoneNumber.hpp"
#include "AbstractController.hpp"
#include "log/log.hpp"

namespace bt::evt
{
    struct Base
    {
        virtual void dispatch(bluetooth::AbstractController *controler) const = 0;
        virtual ~Base()                                                       = default;

      protected:
        Base() = default;
    };

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

    struct StopScan : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct GetDevicesAvailable : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct VisibilityOn : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct VisibilityOff : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct ConnectAudio : public Base
    {
        explicit ConnectAudio(const Devicei &dev) : device(dev)
        {}
        const Devicei device;

        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct DisconnectAudio : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct PowerOn : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct PowerOff : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct ShutDown : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct Pair : public Base
    {
        Pair(const Devicei &device) : device(device)
        {}
        const Devicei device;

        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct Unpair : public Base
    {
        explicit Unpair(const Devicei &device) : device(device)
        {}
        const Devicei device;

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

    struct StartRinging : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct StopRinging : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct StartRouting : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct StartStream : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct StopStream : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct CallAnswered : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct CallTerminated : public Base
    {
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->handle(*this);
        }
    };
    struct CallStarted : public Base
    {
        explicit CallStarted(const utils::PhoneNumber &number) : number(number)
        {}
        const utils::PhoneNumber number;
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->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
        {
            controler->handle(*this);
        }
    };
    struct SignalStrengthData : public Base
    {
        explicit SignalStrengthData(const Store::SignalStrength &strength) : strength(strength)
        {}
        Store::SignalStrength strength;
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->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
        {
            controler->handle(*this);
        }
    };
    struct BatteryLevelData : public Base
    {
        explicit BatteryLevelData(unsigned int level) : level(level)
        {}
        const unsigned int level;
        void dispatch(bluetooth::AbstractController *controler) const override
        {
            controler->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
        {
            controler->handle(*this);
        }
    };
} // namespace bt::evt

A module-bluetooth/Bluetooth/doc/README.md => module-bluetooth/Bluetooth/doc/README.md +35 -0
@@ 0,0 1,35 @@
How to add new command handling in bluetooth
============================================

**remember**
Blutetooth Worker command handling uses [double dispatch](https://www.wikiwand.com/en/Double_dispatch) (if you are not familiar with the technique, see: [double dispatch example](https://dzone.com/articles/double-dispatch-in-c))

# To add a new event:

1. In `event/Events.hpp`
    - Add new event
    - remember to
        - inherit from base event
        - add override on `dispatch` function
    - Events may have const data inside, in such case please:
        - set const data in constructor
        - set data as const in structure
        - do not add needless getter/setter
1. In `AbstractController.hpp`:
    - Add new event to forward declaration
    - Add new event handler in the class below
2. In `WorkerController.{hpp,cpp}`
    - Add new event handler to worker controller implementation
    - Add event handling in the desired state
    - When adding new Handler or Event in the sml state machine please:
        - define structure per each event handler
        - **this way we have it's name in the logs wich** is super helpful
3. In `test` catalog
    - add unit tests regarding the state added
    - **please add tests for both: success and failure** scenarions, not only the green path

# Bluetooth command handling state diagram

# To generate state machine diagram

`make bluetooth_uml` then `./bluetooth_uml > bluetooth_uml.puml` and generate diagram via plantuml from that

A module-bluetooth/Bluetooth/doc/uml/CMakeLists.txt => module-bluetooth/Bluetooth/doc/uml/CMakeLists.txt +8 -0
@@ 0,0 1,8 @@
project(call_uml)
add_executable(${PROJECT_NAME} uml_printer.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
        module-bluetooth
        log
        sml::sml
        sml::utils::logger
)

A module-bluetooth/Bluetooth/doc/uml/uml_printer.cpp => module-bluetooth/Bluetooth/doc/uml/uml_printer.cpp +10 -0
@@ 0,0 1,10 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <BluetoothMachine.hpp>
#include <sml-utils/PlantUML.hpp>

int main()
{
    dump<bluetooth::SM>();
}

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

#pragma once


@@ 17,14 17,15 @@ 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;
        virtual void stopScan()                                                                  = 0;
        virtual void setVisibility(bool visibility)                                              = 0;
        [[nodiscard]] virtual auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool = 0;
        [[nodiscard]] virtual auto unpair(Devicei device) -> bool                                 = 0;
        [[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;
        // TODO make bool again
        virtual void stopScan()                                             = 0;
        virtual void setVisibility(bool visibility)                         = 0;
        virtual void pair(Devicei device, std::uint8_t protectionLevel = 0) = 0;
        virtual void unpair(Devicei device)                                 = 0;

        virtual void registerErrorCallback(const ErrorCallback &newCallback)     = 0;
        virtual void registerPowerOnCallback(const PowerOnCallback &newCallback) = 0;

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp +7 -4
@@ 106,6 106,7 @@ namespace bluetooth
            LOG_INFO("BTstack up and running");
            bluetooth::KeyStorage::settings->setValue(bluetooth::Settings::State,
                                                      static_cast<int>(BluetoothStatus::State::On));
            // TODO inform SM and process ON?
            if (powerOnCallback) {
                powerOnCallback();
            }


@@ 218,13 219,15 @@ namespace bluetooth
    {
        gap->setVisibility(visibility);
    }
    auto Driver::pair(Devicei device, std::uint8_t protectionLevel) -> bool
    void Driver::pair(Devicei device, std::uint8_t protectionLevel)
    {
        LOG_INFO("Device: %s, addr: %s", device.name.data(), device.address_str());
        return gap->pair(device, protectionLevel);
        gap->pair(device, protectionLevel);
    }
    auto Driver::unpair(Devicei device) -> bool

    void Driver::unpair(Devicei device)
    {
        return gap->unpair(device);
        gap->unpair(device);
    }

} // namespace bluetooth

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

#pragma once


@@ 44,7 44,7 @@ namespace bluetooth
        auto scan() -> Error override;
        void stopScan() override;
        void setVisibility(bool visibility) override;
        auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool override;
        auto unpair(Devicei device) -> bool override;
        void pair(Devicei device, std::uint8_t protectionLevel = 0) override;
        void unpair(Devicei device) override;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp +4 -6
@@ 84,17 84,16 @@ namespace bluetooth
        LOG_INFO("Visibility: %s", visibility ? "true" : "false");
    }

    auto GAP::pair(Devicei device, std::uint8_t protectionLevel) -> bool
    void GAP::pair(Devicei device, std::uint8_t protectionLevel)
    {
        if (hci_get_state() == HCI_STATE_WORKING) {
            auto it = devices().find(device.address);
            if (it == devices().end()) {
                LOG_ERROR("device not found: %s", device.address_str());
                return false;
                return;
            }
            return gap_dedicated_bonding(device.address, protectionLevel) == 0;
            gap_dedicated_bonding(device.address, protectionLevel);
        }
        return false;
    }

    void GAP::sendDevices()


@@ 378,7 377,7 @@ namespace bluetooth
        return devices().getList();
    }

    auto GAP::unpair(Devicei device) -> bool
    void GAP::unpair(Devicei device)
    {
        LOG_INFO("Unpairing device");
        gap_drop_link_key_for_bd_addr(device.address);


@@ 386,7 385,6 @@ namespace bluetooth
        LOG_INFO("Device unpaired");
        ownerService->bus.sendMulticast(std::make_shared<message::bluetooth::UnpairResult>(device, true),
                                        sys::BusChannel::BluetoothNotifications);
        return true;
    }

    void GAP::respondPinCode(const std::string &pin, Devicei d)

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp +2 -2
@@ 55,8 55,8 @@ namespace bluetooth
        auto scan() -> Error;
        void stopScan();
        void setVisibility(bool visibility);
        auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool;
        auto unpair(Devicei device) -> bool;
        void pair(Devicei device, std::uint8_t protectionLevel = 0);
        void unpair(Devicei device);
        static auto getDevicesList() -> std::vector<Devicei>;
        static void respondPinCode(const std::string &pin, Devicei d);
        static void finishCodeComparison(bool accepted, Devicei d);

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +6 -8
@@ 105,11 105,10 @@ namespace bluetooth
    {
        return callProfilePtr->callActive();
    }
    auto ProfileManager::setIncomingCallNumber(const DataVariant &data) -> Error::Code
    auto ProfileManager::setIncomingCallNumber(const utils::PhoneNumber &nr) -> Error::Code
    {
        auto number = std::get<utils::PhoneNumber::View>(data);
        if (callProfilePtr) {
            return callProfilePtr->setIncomingCallNumber(number.getE164());
            return callProfilePtr->setIncomingCallNumber(nr.getView().getE164());
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;


@@ 132,9 131,9 @@ namespace bluetooth
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
    }
    auto ProfileManager::setBatteryLevelData(const DataVariant &data) -> Error::Code
    auto ProfileManager::setBatteryLevelData(unsigned int level) -> Error::Code
    {
        auto batteryLevel = std::get<BatteryLevel>(data);
        auto batteryLevel = BatteryLevel(level);
        if (callProfilePtr) {
            return callProfilePtr->setBatteryLevel(batteryLevel);
        }


@@ 158,11 157,10 @@ namespace bluetooth
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;
    }
    auto ProfileManager::callStarted(const DataVariant &data) -> Error::Code
    auto ProfileManager::callStarted(const utils::PhoneNumber &nr) -> Error::Code
    {
        if (callProfilePtr) {
            auto number = std::get<utils::PhoneNumber::View>(data);
            return callProfilePtr->callStarted(number.getE164());
            return callProfilePtr->callStarted(nr.getView().getE164());
        }
        LOG_ERROR("No profile, returning!");
        return Error::NotReady;

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +45 -20
@@ 26,30 26,55 @@ namespace bluetooth

    using ProfileList = std::map<AudioProfile, std::shared_ptr<bluetooth::Profile>>;

    class ProfileManager
    class BaseProfileManager
    {
      public:
        virtual ~BaseProfileManager()                                                            = default;
        virtual auto init() -> Error::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;
    };

    class ProfileManager : public BaseProfileManager
    {
      public:
        explicit ProfileManager(sys::Service *ownerService);
        ProfileManager() = delete;

        auto init() -> Error::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 init() -> Error::Code;
        void deInit();
        auto connect(const Devicei &device) -> Error::Code;
        auto disconnect() -> Error::Code;
        auto start() -> Error::Code;
        auto stop() -> Error::Code;
        auto startRinging() -> Error::Code;
        auto stopRinging() -> Error::Code;
        auto initializeCall() -> Error::Code;
        auto terminateCall() -> Error::Code;
        auto callAnswered() -> Error::Code;
        auto callStarted(const DataVariant &data) -> Error::Code;
        auto setIncomingCallNumber(const DataVariant &data) -> Error::Code;
        auto setSignalStrengthData(const DataVariant &data) -> Error::Code;
        auto setOperatorNameData(const DataVariant &data) -> Error::Code;
        auto setBatteryLevelData(const DataVariant &data) -> Error::Code;
        auto setNetworkStatusData(const DataVariant &data) -> Error::Code;

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

      private:
        sys::Service *ownerService;

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +1 -7
@@ 32,13 32,6 @@ set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/PhoneInterface.cpp

    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/Command.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/DeviceData.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/PhoneNumberData.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/SignalStrengthData.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/OperatorNameData.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/BatteryLevelData.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/command/NetworkStatusData.cpp

        )

include(lib/btstack.cmake)


@@ 82,6 75,7 @@ target_link_libraries(${PROJECT_NAME}
    service-bluetooth
    service-evtmgr
    sml::sml
    sml::utils::logger
    json::json
)


M module-bluetooth/tests/CMakeLists.txt => module-bluetooth/tests/CMakeLists.txt +1 -1
@@ 5,12 5,12 @@ add_catch2_executable(
        tests-StatefulController.cpp
        tests-BluetoothDevicesModel.cpp
        tests-Devicei.cpp
        tests-command.cpp
        tests-BTKeysStorage.cpp
    LIBS
        module-sys
        module-bluetooth
        service-bluetooth
        test::fakeit
)



M module-bluetooth/tests/tests-StatefulController.cpp => module-bluetooth/tests/tests-StatefulController.cpp +230 -157
@@ 2,188 2,261 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include "WorkerController.hpp"
#include <catch/fakeit.hpp>
#include "service-bluetooth/SettingsHolder.hpp"
#include "BluetoothStateMachine.hpp"

using namespace bluetooth;

class DriverMock : public AbstractDriver
auto driver()
{
  public:
    Error::Code init() override
    {
        return initReturnCode;
    }
    Error::Code run() override
    {
        return runReturnCode;
    }
    Error::Code stop() override
    {
        return stopReturnCode;
    }
    Error scan() override
    {
        return Error::Success;
    }
    void stopScan() override
    {}
    void setVisibility(bool visibility) override
    {}
    bool pair(Devicei device, std::uint8_t protectionLevel = 0) override
    {
        return true;
    }
    bool unpair(Devicei device) override
    {
        return true;
    }
    void registerErrorCallback(const ErrorCallback &) override
    {}
    void registerPowerOnCallback(const PowerOnCallback &) override
    {}

    Error::Code initReturnCode = Error::Success;
    Error::Code runReturnCode  = Error::Success;
    Error::Code stopReturnCode = Error::Success;
    fakeit::Mock<AbstractDriver> mock;
    fakeit::When(Method(mock, init)).AlwaysReturn(Error::Code::Success);
    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, stopScan)).AlwaysReturn();
    fakeit::When(Method(mock, setVisibility)).AlwaysReturn();
    fakeit::When(Method(mock, pair)).AlwaysReturn();
    fakeit::When(Method(mock, unpair)).AlwaysReturn();
    fakeit::When(Method(mock, registerErrorCallback)).AlwaysReturn();
    fakeit::When(Method(mock, registerPowerOnCallback)).AlwaysReturn();
    return mock;
};

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

class HandlerMock : public AbstractCommandHandler
auto handler()
{
  public:
    Error::Code handle(Command &command) override
    {
        return returnCode;
    }

    Error::Code returnCode = Error::Success;
    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, establishAudioConnection)).AlwaysReturn(Error::Code::Success);
    fakeit::When(Method(mock, disconnectAudioConnection)).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);
    return mock;
};

TEST_CASE("Given StatefulController when turn on then turned on")
template <typename T> auto mock_to_shared(T *val)
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());
    auto t = std::shared_ptr<T>(val, [](...) {});
    return t;
}

TEST_CASE("Given StatefulController when error during device registration then turned off")
/// TODO nei wiem czemu to nie działą (double free)
template <typename T> class Mock
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), []() { return Error::SystemError; }};
    controller.turnOn();
    REQUIRE(!controller.isOn());
}
    T t;
    std::shared_ptr<std::remove_reference_t<decltype(t.get())>> shared_;

TEST_CASE("Given StatefulController when error during driver init then turned off")
{
    auto driver            = std::make_unique<DriverMock>();
    driver->initReturnCode = Error::SystemError;
  public:
    explicit Mock(T t) : t(t)
    {}

    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(!controller.isOn());
}
    auto shared()
    {
        return shared_;
    }
};

TEST_CASE("Given StatefulController when error during driver run then turned off")
namespace mock
{
    auto driver           = std::make_unique<DriverMock>();
    driver->runReturnCode = Error::SystemError;

    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(!controller.isOn());
}
    auto settings()
    {
        fakeit::Mock<bluetooth::SettingsHolder> mock;
        fakeit::When(Method(mock, getValue)).AlwaysReturn({});
        fakeit::When(Method(mock, setValue)).AlwaysReturn();
        return mock;
    }

TEST_CASE("Given StatefulController when restart then don't init twice")
{
    auto driver    = std::make_shared<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{driver, std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());

    controller.turnOff();
    REQUIRE(!controller.isOn());

    driver->initReturnCode = Error::SystemError;
    controller.turnOn();
    REQUIRE(controller.isOn());
}
    auto devices()
    {
        return std::make_shared<std::vector<Devicei>>();
    }

TEST_CASE("Given StatefulController when turn off in off state then turned off")
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOff();
    REQUIRE(!controller.isOn());
}
    auto profile()
    {
        fakeit::Mock<bluetooth::BaseProfileManager> mock;
        fakeit::When(Method(mock, init)).AlwaysReturn(Error::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);
        return mock;
    };

TEST_CASE("Given StatefulController when turn off in on state then turned off")
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());

    controller.turnOff();
    REQUIRE(!controller.isOn());
}
} // namespace mock

TEST_CASE("Given StatefulController when shutdown in off state then terminated")
TEST_CASE("Given StatefulController when turn on then turned on")
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.shutdown();
    REQUIRE(controller.isTerminated());
}
    auto d         = driver();
    auto drive     = mock_to_shared(&d.get());
    auto h         = handler();
    auto processor = mock_to_shared(&h.get());
    auto s         = mock::settings();
    auto sett      = mock_to_shared(&s.get());
    auto devs      = mock::devices();
    auto p         = mock::profile();
    auto profile   = mock_to_shared(&p.get());
    // auto profile = Mock(mock::profile());

TEST_CASE("Given StatefulController when shutdown in on state then terminated")
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());

    controller.shutdown();
    REQUIRE(controller.isTerminated());
}
    StatefulController::Impl controller(drive, processor, InitializerMock, sett, devs, profile);

TEST_CASE("Given StatefulController when process command successfully then turned on")
{
    auto driver    = std::make_unique<DriverMock>();
    auto processor = std::make_unique<HandlerMock>();
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());

    auto command = bluetooth::Command(Command::Type::PowerOn);
    controller.processCommand(command);
    REQUIRE(controller.isOn());
    controller.sm.process_event(bt::evt::PowerOn{});
    controller.sm.process_event(bt::evt::PowerOff{});
    // TODO assert driver initialized
    // TODO test for some loaded devices with some other mockup
    // TODO move machine to Impl and assert for next states
    // using namespace boost::sml; REQUIRE(controller->impl.is("Shutdown"_s));
}

TEST_CASE("Given StatefulController when processing command failed then restarted and turned on")
{
    auto driver           = std::make_unique<DriverMock>();
    auto processor        = std::make_unique<HandlerMock>();
    processor->returnCode = Error::SystemError;
    StatefulController controller{std::move(driver), std::move(processor), InitializerMock};
    controller.turnOn();
    REQUIRE(controller.isOn());

    auto command = bluetooth::Command(Command::Type::PowerOn);
    controller.processCommand(command);
    controller.processCommand(command);

    REQUIRE(controller.isOn());
}
// TEST_CASE("pair")
// {
//     auto d = driver();
//     auto drive =  mock_to_shared(&d.get());
//     auto h = handler();
//     auto processor = mock_to_shared(&h.get());
//     auto s = mock::settings();
//     auto sett = mock_to_shared(&s.get());
//     auto devs = mock::devices();
//     auto profile = Mock(mock::profile());
//
//     StatefulController controller(drive, processor, InitializerMock, sett, devs, profile.shared());
//     controller.handle(bt::evt::PowerOn{});
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
//     // TODO test - no pair device in settings
// }
//
// TEST_CASE("unpair")
// {
//     auto d = driver();
//     auto drive =  mock_to_shared(&d.get());
//     auto h = handler();
//     auto processor = mock_to_shared(&h.get());
//     auto s = mock::settings();
//     auto sett = mock_to_shared(&s.get());
//     auto devs = mock::devices();
//     auto profile = Mock(mock::profile());
//     StatefulController controller(drive, processor, InitializerMock, sett, devs, profile.shared());
//
//     controller.handle(bt::evt::PowerOn{});
//     // TODO here nothing happens
//     controller.handle(bt::evt::Unpair{Devicei{"lol"}});
//     // TODO here - device added
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
//     // TODO here device removed
//     controller.handle(bt::evt::Pair{Devicei{"lol"}});
// }
//
//
//
// // TEST_CASE("Given StatefulController when error during device registration then turned off")
// // {
// //     auto h = handler();
// //     auto processor = mock_to_shared(&h);
// //
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, []() { return Error::SystemError; }};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when error during driver init then turned off")
// // {
// //     driver->initReturnCode = Error::SystemError;
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when error during driver run then turned off")
// // {
// //     driver->runReturnCode = Error::SystemError;
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when restart then don't init twice")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{driver, processor, InitializerMock};
// //
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// //     driver->initReturnCode = Error::SystemError;
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // TEST_CASE("Given StatefulController when turn off in off state then turned off")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOff{});
// // }
// //
// // TEST_CASE("Given StatefulController when turn off in on state then turned off")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// // }
// //
// // TEST_CASE("Given StatefulController when shutdown in off state then terminated")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOff{});
// //     REQUIRE(controller.isTerminated());
// // }
// //
// // TEST_CASE("Given StatefulController when shutdown in on state then terminated")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// //     controller.handle(bt::evt::PowerOff{});
// //     REQUIRE(controller.isTerminated());
// // }
// //
// // TEST_CASE("Given StatefulController when process command successfully then turned on")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //     controller.handle(bt::evt::PowerOn{});
// // }
// //
// // // TODO
// // TEST_CASE("Given StatefulController when processing command failed then restarted and turned on")
// // {
// //     auto h = handler(); auto processor = mock_to_shared(&h);
// //     auto d = driver(); auto drive =  mock_to_shared(&d);
// //     StatefulController controller{drive, processor, InitializerMock};
// //
// //     controller.handle(bt::evt::PowerOn{});
// // }

M module-bluetooth/tests/tests-command.cpp => module-bluetooth/tests/tests-command.cpp +0 -1
@@ 4,7 4,6 @@
#include <catch2/catch.hpp>
#include "btstack_util.h"
#include <command/Command.hpp>
#include <command/DeviceData.hpp>
#include <command/PhoneNumberData.hpp>
#include <command/BatteryLevelData.hpp>


M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +50 -65
@@ 40,12 40,6 @@
#include <service-bluetooth/messages/Authenticate.hpp>
#include <GAP/GAP.hpp>
#include <service-cellular/CellularMessage.hpp>
#include <command/PhoneNumberData.hpp>
#include <command/DeviceData.hpp>
#include <command/SignalStrengthData.hpp>
#include <command/OperatorNameData.hpp>
#include <command/BatteryLevelData.hpp>
#include <command/NetworkStatusData.hpp>
#include <service-evtmgr/BatteryMessages.hpp>

namespace


@@ 145,8 139,8 @@ sys::ReturnCodes ServiceBluetooth::DeinitHandler()

void ServiceBluetooth::ProcessCloseReason(sys::CloseReason closeReason)
{
    sendWorkerCommand(bluetooth::Command::Type::DisconnectAudio);
    sendWorkerCommand(bluetooth::Command::Type::PowerOff);
    sendWorkerCommand(new bt::evt::DisconnectAudio());
    sendWorkerCommand(new bt::evt::PowerOff());
}

sys::MessagePointer ServiceBluetooth::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg,


@@ 161,16 155,6 @@ sys::ReturnCodes ServiceBluetooth::SwitchPowerModeHandler(const sys::ServicePowe
    return sys::ReturnCodes::Success;
}

void ServiceBluetooth::sendWorkerCommand(bluetooth::Command::Type commandType,
                                         std::unique_ptr<bluetooth::CommandData> data)
{
    bluetooth::Command::CommandPack pack;
    pack.data        = std::move(data);
    pack.commandType = commandType;
    xQueueSend(workerQueue, &pack, portMAX_DELAY);
    pack.data.release();
}

auto ServiceBluetooth::handle(BluetoothAudioStartMessage *msg) -> std::shared_ptr<sys::Message>
{
    worker->setAudioDevice(msg->getAudioDevice());


@@ 209,7 193,7 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
    case BluetoothStatus::State::On:

        cpuSentinel->HoldMinimumFrequency(bsp::CpuFrequencyMHz::Level_3);
        sendWorkerCommand(bluetooth::Command::Type::PowerOn);
        sendWorkerCommand(new bt::evt::PowerOn());
        bus.sendMulticast(
            std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Enabled),
            sys::BusChannel::BluetoothModeChanges);


@@ 228,8 212,12 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
    default:
        break;
    }
    sendWorkerCommand(newBtStatus.visibility ? bluetooth::Command::Type::VisibilityOn
                                             : bluetooth::Command::Type::VisibilityOff);
    if (newBtStatus.visibility) {
        sendWorkerCommand(new bt::evt::VisibilityOn());
    }
    else {
        sendWorkerCommand(new bt::evt::VisibilityOff());
    }
    return sys::MessageNone{};
}



@@ 237,9 225,8 @@ auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys:
{
    auto device = msg->getDevice();
    bluetoothDevicesModel->removeDevice(device);
    auto commandData = std::make_unique<bluetooth::DeviceData>(device);

    sendWorkerCommand(bluetooth::Command::Type::Pair, std::move(commandData));
    sendWorkerCommand(new bt::evt::Pair(device));

    device.deviceState = DeviceState::Pairing;
    bluetoothDevicesModel->insertDevice(device);


@@ 268,12 255,17 @@ auto ServiceBluetooth::handle(BluetoothPairResultMessage *msg) -> std::shared_pt
    return sys::MessageNone{};
}

void ServiceBluetooth::sendWorkerCommand(bt::evt::Base *command)
{
    if (workerQueue != nullptr) {
        workerQueue->push(bluetooth::Command{command});
    }
}

auto ServiceBluetooth::handle(message::bluetooth::Unpair *msg) -> std::shared_ptr<sys::Message>
{
    auto commandData = std::make_unique<bluetooth::DeviceData>(msg->getDevice());
    sendWorkerCommand(bluetooth::Command::Type::Unpair, std::move(commandData));
    sendWorkerCommand(new bt::evt::Unpair(msg->getDevice()));
    bluetoothDevicesModel->removeDevice(msg->getDevice());

    return sys::MessageNone{};
}



@@ 293,12 285,10 @@ auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::sh
    auto newName = msg->getName();
    bluetooth::set_name(newName);
    settingsHolder->setValue(bluetooth::Settings::DeviceName, newName);
    sendWorkerCommand(bluetooth::Command::Type::PowerOff);
    sendWorkerCommand(new bt::evt::PowerOff());

    btRestartTimer =
        sys::TimerFactory::createSingleShotTimer(this, "btRestartTimer", btRestartDelay, [this](sys::Timer &_) {
            sendWorkerCommand(bluetooth::Command::Type::PowerOn);
        });
    btRestartTimer = sys::TimerFactory::createSingleShotTimer(
        this, "btRestartTimer", btRestartDelay, [this](sys::Timer &_) { sendWorkerCommand(new bt::evt::PowerOn()); });
    btRestartTimer.start();

    return sys::MessageNone{};


@@ 307,9 297,7 @@ auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::sh
auto ServiceBluetooth::handle(message::bluetooth::Connect *msg) -> std::shared_ptr<sys::Message>
{
    auto device = msg->getDevice();
    auto commandData = std::make_unique<bluetooth::DeviceData>(msg->getDevice());
    sendWorkerCommand(bluetooth::Command::Type::ConnectAudio, std::move(commandData));

    sendWorkerCommand(new bt::evt::ConnectAudio(device));
    bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Connecting);
    bluetoothDevicesModel->syncDevicesWithApp();
    return sys::MessageNone{};


@@ 359,7 347,7 @@ auto ServiceBluetooth::handle(message::bluetooth::ConnectResult *msg) -> std::sh

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::Disconnect *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(bluetooth::Command::Type::DisconnectAudio);
    sendWorkerCommand(new bt::evt::DisconnectAudio());
    return sys::MessageNone{};
}



@@ 417,31 405,33 @@ auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Mes
    resetTimeoutTimer();

    switch (msg->req) {
    case BluetoothMessage::Start:
        break;
    case BluetoothMessage::Scan:
        sendWorkerCommand(bluetooth::Command::Type::StartScan);
        sendWorkerCommand(new bt::evt::StartScan());
        break;
    case BluetoothMessage::StopScan:
        sendWorkerCommand(bluetooth::Command::Type::StopScan);
        sendWorkerCommand(new bt::evt::StopScan());
        break;
    case BluetoothMessage::getDevicesAvailable:
        sendWorkerCommand(bluetooth::Command::Type::getDevicesAvailable);
        sendWorkerCommand(new bt::evt::GetDevicesAvailable());
        break;
    case BluetoothMessage::Visible: {
        auto visibility =
            not std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility));
        sendWorkerCommand(visibility ? bluetooth::Command::Type::VisibilityOn
                                     : bluetooth::Command::Type::VisibilityOff);
        if (visibility) {
            sendWorkerCommand(new bt::evt::VisibilityOn());
        }
        else {
            sendWorkerCommand(new bt::evt::VisibilityOff());
        }
    } break;
    case BluetoothMessage::Play:
        sendWorkerCommand(bluetooth::Command::Type::StartStream);
        sendWorkerCommand(new bt::evt::StartStream());
        break;
    case BluetoothMessage::Disconnect:
        sendWorkerCommand(bluetooth::Command::Type::DisconnectAudio);
        sendWorkerCommand(new bt::evt::DisconnectAudio());
        break;
    case BluetoothMessage::Stop:
        sendWorkerCommand(bluetooth::Command::Type::StopStream);
        sendWorkerCommand(new bt::evt::StopStream());
        break;
    default:
        break;


@@ 452,15 442,14 @@ auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Mes

auto ServiceBluetooth::handle(BluetoothAddrMessage *msg) -> std::shared_ptr<sys::Message>
{
    auto commandData = std::make_unique<bluetooth::DeviceData>(msg->device);
    sendWorkerCommand(bluetooth::Command::Type::ConnectAudio, std::move(commandData));
    sendWorkerCommand(new bt::evt::ConnectAudio(msg->device));
    return std::make_shared<sys::ResponseMessage>();
}

auto ServiceBluetooth::handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr<sys::Message>
{
    if (typeid(*msg->event) == typeid(sdesktop::bluetooth::GetAvailableDevicesEvent)) {
        sendWorkerCommand(bluetooth::Command::Type::getDevicesAvailable);
        sendWorkerCommand(new bt::evt::GetDevicesAvailable());
    }
    return sys::MessageNone{};
}


@@ 487,7 476,7 @@ auto ServiceBluetooth::handle(message::bluetooth::HFPVolume *msg) -> std::shared

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



@@ 499,8 488,7 @@ auto ServiceBluetooth::handle(CellularCallerIdMessage *msg) -> std::shared_ptr<s

    if (btOn) {
        LOG_DEBUG("Sending to profile!");
        auto commandData = std::make_unique<bluetooth::PhoneNumberData>(number);
        sendWorkerCommand(bluetooth::Command::Type::IncomingCallNumber, std::move(commandData));
        sendWorkerCommand(new bt::evt::IncomingCallNumber(number));
    }

    return sys::MessageNone{};


@@ 508,7 496,7 @@ auto ServiceBluetooth::handle(CellularCallerIdMessage *msg) -> std::shared_ptr<s

auto ServiceBluetooth::handle(CellularCallActiveNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(bluetooth::Command::Type::CallAnswered);
    sendWorkerCommand(new bt::evt::CallAnswered());
    return std::make_shared<sys::ResponseMessage>();
}



@@ 516,8 504,7 @@ auto ServiceBluetooth::handle(CellularSignalStrengthUpdateNotification *msg) -> 
{
    auto signalStrength = Store::GSM::get()->getSignalStrength();
    LOG_DEBUG("Bluetooth: RSSI %d/5", static_cast<int>(signalStrength.rssiBar));
    auto commandData = std::make_unique<bluetooth::SignalStrengthData>(signalStrength);
    sendWorkerCommand(bluetooth::Command::Type::SignalStrengthData, std::move(commandData));
    sendWorkerCommand(new bt::evt::SignalStrengthData(signalStrength));
    return std::make_shared<sys::ResponseMessage>();
}



@@ 525,8 512,7 @@ auto ServiceBluetooth::handle(CellularCurrentOperatorNameNotification *msg) -> s
{
    auto opName = msg->getCurrentOperatorName();
    LOG_DEBUG("Bluetooth: Operator name: %s", opName.c_str());
    auto commandData = std::make_unique<bluetooth::OperatorNameData>(bluetooth::OperatorName(opName));
    sendWorkerCommand(bluetooth::Command::Type::OperatorNameData, std::move(commandData));
    sendWorkerCommand(new bt::evt::OperatorNameData(opName));
    return std::make_shared<sys::ResponseMessage>();
}



@@ 554,7 540,8 @@ void ServiceBluetooth::resetTimeoutTimer()

void ServiceBluetooth::handleTurnOff()
{
    sendWorkerCommand(bluetooth::Command::Type::PowerOff);
    sendWorkerCommand(new bt::evt::PowerOff());
    // NOTE: This should be in bluetooth state machine
    cpuSentinel->ReleaseMinimumFrequency();
    bus.sendMulticast(std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Disabled),
                      sys::BusChannel::BluetoothModeChanges);


@@ 574,33 561,31 @@ auto ServiceBluetooth::handle(sevm::BatteryStatusChangeMessage *msg) -> std::sha
{
    auto batteryLevel = Store::Battery::get().level;
    LOG_DEBUG("Bluetooth: Battery level %d", batteryLevel);
    auto commandData = std::make_unique<bluetooth::BatteryLevelData>(bluetooth::BatteryLevel(batteryLevel));
    sendWorkerCommand(bluetooth::Command::Type::BatteryLevelData, std::move(commandData));
    sendWorkerCommand(new bt::evt::BatteryLevelData(batteryLevel));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallEndedNotification *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(bluetooth::Command::Type::CallTerminated);
    sendWorkerCommand(new bt::evt::CallTerminated());
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(CellularNetworkStatusUpdateNotification *msg) -> std::shared_ptr<sys::Message>
{
    auto status = Store::GSM::get()->getNetwork().status;
    LOG_DEBUG("Bluetooth: Network status %s", magic_enum::enum_name(status).data());
    auto commandData = std::make_unique<bluetooth::NetworkStatusData>(status);
    sendWorkerCommand(bluetooth::Command::Type::NetworkStatusData, std::move(commandData));
    sendWorkerCommand(new bt::evt::NetworkStatusData(status));
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(cellular::CallStartedNotification *msg) -> std::shared_ptr<sys::Message>
{
    if (!msg->isCallIncoming()) {
        auto commandData = std::make_unique<bluetooth::PhoneNumberData>(msg->getNumber());
        sendWorkerCommand(bluetooth::Command::Type::CallStarted, std::move(commandData));
        auto evt = new bt::evt::CallStarted(msg->getNumber());
        sendWorkerCommand(std::move(evt));
    }
    return sys::MessageNone{};
}
auto ServiceBluetooth::handle(CellularIncominCallMessage *msg) -> std::shared_ptr<sys::Message>
{
    sendWorkerCommand(bluetooth::Command::Type::StartRinging);
    sendWorkerCommand(new bt::evt::StartRinging());
    return sys::MessageNone{};
}

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +0 -1
@@ 29,7 29,6 @@ class BluetoothMessage : public sys::DataMessage
    enum Request
    {
        None,
        Start,
        Scan,
        StopScan,
        getDevicesAvailable,

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +9 -4
@@ 7,7 7,9 @@
#include <system/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include "Service/Mailbox.hpp"
#include "service-bluetooth/SettingsHolder.hpp"
#include "service-bluetooth/WorkerLock.hpp"
#include <service-db/DBServiceName.hpp>
#include <service-audio/ServiceAudio.hpp>
#include <module-bluetooth/Bluetooth/CommandHandler.hpp>


@@ 87,12 89,14 @@ class ServiceBluetooth : public sys::Service
    sys::ReturnCodes DeinitHandler() override;
    void ProcessCloseReason(sys::CloseReason closeReason) override;
    virtual sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;
    void sendWorkerCommand(bluetooth::Command::Type commandType,
                           std::unique_ptr<bluetooth::CommandData> data = nullptr);

    void sendWorkerCommand(bt::evt::Base *command);

    void handleTurnOff();
    QueueHandle_t workerQueue = nullptr;

    std::shared_ptr<Mailbox<bluetooth::Command, QueueHandle_t, WorkerLock>> workerQueue;
    std::shared_ptr<bluetooth::SettingsHolder> settingsHolder;
    bluetooth::ProfileManager *profileManagerPtr = nullptr;
    bluetooth::BaseProfileManager *profileManagerPtr = nullptr;

  private:
    std::unique_ptr<BluetoothWorker> worker;


@@ 114,6 118,7 @@ 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>;

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

#pragma once

#include "log/log.hpp"
#include <FreeRTOS.h>
#include <queue.h>
#include <mutex.hpp>

class WorkerLock
{
    QueueHandle_t queue;
    cpp_freertos::MutexStandard &mutex;

  public:
    WorkerLock(QueueHandle_t queue, cpp_freertos::MutexStandard &mutex) : queue(queue), mutex(mutex)
    {}

    bool wait()
    {
        mutex.Unlock();
        auto ret = xQueueReceive(queue, nullptr, portMAX_DELAY) != pdTRUE;
        mutex.Lock();
        return ret;
    }

    bool wait(TickType_t ticks)
    {
        mutex.Unlock();
        auto ret = xQueueReceive(queue, nullptr, ticks) != pdTRUE;
        mutex.Lock();
        return ret;
    }

    bool signal()
    {
        return xQueueSend(queue, nullptr, portMAX_DELAY);
    }
};

M module-sys/Service/include/Service/Mailbox.hpp => module-sys/Service/include/Service/Mailbox.hpp +46 -23
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 7,22 7,49 @@
#include "thread.hpp"
#include <mutex.hpp>
#include <condition_variable.hpp>
#include <optional>

template <typename T> class Mailbox
class ServiceLock
{
    cpp_freertos::Thread *thread;
    cpp_freertos::MutexStandard &mutex_;
    cpp_freertos::ConditionVariable cond_;

  public:
    Mailbox(cpp_freertos::Thread *thread) : thread_(thread)
    ServiceLock(cpp_freertos::Thread *thread, cpp_freertos::MutexStandard &mutex_) : thread(thread), mutex_(mutex_)
    {}

    T peek()
    bool wait()
    {
        return thread->Wait(cond_, mutex_);
    }

    bool wait(TickType_t ticks)
    {
        return thread->Wait(cond_, mutex_, ticks);
    }
    bool signal()
    {
        cond_.Signal();
        return true;
    }
};

template <typename T, typename Base = cpp_freertos::Thread *, typename Lock = ServiceLock> class Mailbox
{
  public:
    Mailbox(Base thread) : thread_(thread)
    {}

    std::optional<T> peek()
    {
        cpp_freertos::LockGuard mlock(mutex_);
        while (queue_.empty()) {
            thread_->Wait(cond_, mutex_);
        if (queue_.empty()) {
            return {};
        }
        auto item = queue_.front();
        return item;
        auto el = queue_.front();
        queue_.pop_front();
        return {el};
    }

    T pop(uint32_t timeout = portMAX_DELAY)


@@ 30,7 57,7 @@ template <typename T> class Mailbox

        cpp_freertos::LockGuard mlock(mutex_);
        while (queue_.empty()) {
            if (thread_->Wait(cond_, mutex_, timeout) == false) {
            if (lock.wait(timeout) == false) {
                return nullptr;
            }
        }


@@ 39,16 66,6 @@ template <typename T> class Mailbox
        return item;
    }

    void pop(T &item)
    {
        cpp_freertos::LockGuard mlock(mutex_);
        while (queue_.empty()) {
            thread_->Wait(cond_, mutex_);
        }
        item = queue_.front();
        queue_.pop_front();
    }

    void push_front(const T &item)
    {
        mutex_.Lock();


@@ 61,7 78,7 @@ template <typename T> class Mailbox
        mutex_.Lock();
        queue_.push_back(item);
        mutex_.Unlock();
        cond_.Signal();
        lock.signal();
    }

    void push(T &&item)


@@ 69,12 86,18 @@ template <typename T> class Mailbox
        mutex_.Lock();
        queue_.push_back(std::move(item));
        mutex_.Unlock();
        cond_.Signal();
        lock.signal();
    }

    bool empty()
    {
        return queue_.empty();
    }

  private:
    cpp_freertos::Thread *thread_;
    Base thread_;
    std::deque<T> queue_;
    cpp_freertos::MutexStandard mutex_;
    cpp_freertos::ConditionVariable cond_;

    Lock lock{thread_, mutex_};
};

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +1 -1
@@ 33,7 33,7 @@ namespace Log
            {"ServiceAntenna", logger_level::LOGERROR},
            {"ServiceAudio", logger_level::LOGINFO},
            {"ServiceBluetooth", logger_level::LOGINFO},
            {"ServiceBluetooth_w1", logger_level::LOGINFO},
            {"ServiceBluetooth_w1", logger_level::LOGDEBUG},
            {"ServiceFota", logger_level::LOGINFO},
            {"ServiceEink", logger_level::LOGINFO},
            {"ServiceDB", logger_level::LOGINFO},