~aleteoryx/muditaos

46b89140540ad9eaca3e5ae39838b158488cbf80 — Michał Kamoń 4 years ago cf75cfc
[EGD-5945] Bluetooth-harness API messages

This PR:
* provides Bluetooth-Harness API messages definition [EGD-5944]
* provides Bluetooth-Harness API messages implementation
* provides Bluetooth-Harness API usage on harness side [EGD-5946]
* provides Bluetooth-Harness tests [EGD-5947]
29 files changed, 885 insertions(+), 412 deletions(-)

M module-apps/application-settings/ApplicationSettings.cpp
M module-bluetooth/Bluetooth/CommandHandler.cpp
M module-bluetooth/Bluetooth/CommandHandler.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.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
M module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp
M module-services/service-desktop/CMakeLists.txt
M module-services/service-desktop/DesktopMessages.cpp
M module-services/service-desktop/README.md
M module-services/service-desktop/ServiceDesktop.cpp
M module-services/service-desktop/endpoints/bluetooth/BluetoothEndpoint.cpp
A module-services/service-desktop/endpoints/bluetooth/BluetoothEventMessages.cpp
A module-services/service-desktop/endpoints/bluetooth/BluetoothEventMessages.hpp
M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp
M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.hpp
A module-services/service-desktop/endpoints/bluetooth/BluetoothMessagesHandler.cpp
A module-services/service-desktop/endpoints/bluetooth/BluetoothMessagesHandler.hpp
M module-services/service-desktop/service-desktop/DesktopMessages.hpp
M module-services/service-desktop/service-desktop/DeveloperModeMessage.hpp
M module-services/service-desktop/service-desktop/ServiceDesktop.hpp
M source/MessageType.hpp
A test/pytest/service-bluetooth/bt_fixtures.py
M test/pytest/service-bluetooth/bt_utils.py
A test/pytest/service-bluetooth/test_basic_control_dev_perspective.py
A test/pytest/service-bluetooth/test_pairing_dev_perspective.py
R test/pytest/service-bluetooth/{test_pairing => test_pairing_hmi_perspective}.py
D test/pytest/service-desktop/test_bluetooth.py
M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +3 -3
@@ 30,6 30,7 @@
#include <i18n/i18n.hpp>
#include <service-evtmgr/EventManagerServiceAPI.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-db/Settings.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>



@@ 63,10 64,9 @@ namespace app
        if (reinterpret_cast<sys::ResponseMessage *>(retMsg.get())->retCode == sys::ReturnCodes::Success) {
            return retMsg;
        }
        if (auto btmsg = dynamic_cast<BluetoothScanResultMessage *>(msgl); btmsg != nullptr) {
            auto devices = btmsg->devices;
        if (auto btmsg = dynamic_cast<message::bluetooth::ResponseVisibleDevices *>(msgl); btmsg != nullptr) {
            LOG_INFO("received BT Scan message!");
            auto data = std::make_unique<gui::DeviceData>(devices);
            auto data = std::make_unique<gui::DeviceData>(btmsg->getDevices());
            windowsFactory.build(this, gui::name::window::name_btscan);
            switchWindow(gui::name::window::name_btscan, gui::ShowMode::GUI_SHOW_INIT, std::move(data));


M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +14 -8
@@ 2,16 2,19 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CommandHandler.hpp"
#include <service-bluetooth/ServiceBluetooth.hpp>

#include <utility>
#include <service-bluetooth/ServiceBluetooth.hpp>
#include <service-bluetooth/SettingsHolder.hpp>

#include <Service/Service.hpp>

#include "Device.hpp"
#include "BtCommand.hpp"

#include <service-desktop/service-desktop/Constants.hpp>
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include "GAP/GAP.hpp"

extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>


@@ 42,6 45,8 @@ namespace bluetooth
            return Error::Success;
        case bluetooth::Command::StartScan:
            return scan();
        case bluetooth::Command::getDevicesAvailable:
            return availableDevices();
        case bluetooth::Command::StopScan:
            return stopScan();
        case bluetooth::Command::StartPan:


@@ 83,7 88,6 @@ namespace bluetooth
        }

        LOG_INFO("Scan started!");
        static_cast<ServiceBluetooth *>(service)->scanStartedCallback();
        // open new scan window
        return Error::Success;
    }


@@ 92,7 96,6 @@ namespace bluetooth
    {
        LOG_INFO("Stopping scan!");
        driver->stopScan();
        static_cast<ServiceBluetooth *>(service)->scanStoppedCallback();
        return Error::Success;
    }



@@ 117,16 120,12 @@ namespace bluetooth
    {
        profileManager->init();
        LOG_INFO("Connecting audio with %s", bd_addr_to_str(addr));
        std::string devAddr{bd_addr_to_str(addr)};
        settings->setValue(bluetooth::Settings::ConnectedDevice, std::move(devAddr));
        profileManager->connect(addr);

        return Error::Success;
    }

    Error::Code CommandHandler::disconnectAudioConnection()
    {
        settings->setValue(bluetooth::Settings::ConnectedDevice, std::string());
        profileManager->disconnect();
        return Error::Success;
    }


@@ 157,4 156,11 @@ namespace bluetooth
        return driver->unpair(addr) ? Error::Success : Error::LibraryError;
    }

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

        return Error::Success;
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +3 -1
@@ 12,6 12,7 @@
namespace sys
{
    class Service;
    class Message;
}

namespace bluetooth


@@ 24,6 25,7 @@ namespace bluetooth
        {
            StartScan,
            StopScan,
            getDevicesAvailable,
            StartPan,
            VisibilityOn,
            VisibilityOff,


@@ 91,7 93,7 @@ namespace bluetooth
        Error::Code disconnectAudioConnection();
        Error::Code pair(bd_addr_t addr);
        Error::Code unpair(bd_addr_t addr);

        Error::Code availableDevices();
        Error::Code switchAudioProfile();
        sys::Service *service;
        std::shared_ptr<bluetooth::SettingsHolder> settings;

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +5 -3
@@ 18,6 18,7 @@
#include <service-evtmgr/Constants.hpp>
#include <log/log.hpp>
#include "service-bluetooth/messages/Connect.hpp"
#include "service-bluetooth/messages/Disconnect.hpp"
#include "service-bluetooth/Constants.hpp"
extern "C"
{


@@ 336,7 337,7 @@ namespace bluetooth
                auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
                busProxy.sendUnicast(
                    std::make_shared<message::bluetooth::ConnectResult>(std::move(deviceAddress), false),
                    "ApplicationSettingsNew");
                    service::name::bluetooth);
                break;
            }
            AVRCP::mediaTracker.a2dp_cid = cid;


@@ 349,7 350,7 @@ namespace bluetooth
            isConnected = true;
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(std::move(deviceAddress), true),
                                 "ApplicationSettingsNew");
                                 service::name::bluetooth);
            break;
        }
        case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: {


@@ 572,7 573,8 @@ namespace bluetooth
                AVRCP::mediaTracker.a2dp_cid  = 0;
                LOG_INFO("A2DP Source: Signaling released.\n\n");
                auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
                busProxy.sendUnicast(std::make_shared<BluetoothDeviceDisconnectedMessage>(), service::name::bluetooth);
                busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(),
                                     service::name::bluetooth);
            }
            isConnected = false;
            break;

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +207 -168
@@ 14,7 14,9 @@
#include "service-bluetooth/messages/Disconnect.hpp"
#include "service-bluetooth/messages/Status.hpp"
#include "service-bluetooth/messages/SetStatus.hpp"
#include <service-bluetooth/messages/BondedDevices.hpp>
#include "service-bluetooth/messages/BondedDevices.hpp"
#include "service-bluetooth/messages/Unpair.hpp"
#include "service-bluetooth/messages/SetDeviceName.hpp"

#include <log/log.hpp>
#include "SystemManager/messages/SentinelRegistrationMessage.hpp"


@@ 22,15 24,17 @@
#include <bits/exception.h>
#include <utility>
#include <service-desktop/service-desktop/DesktopMessages.hpp>
#include <service-desktop/service-desktop/Constants.hpp>
#include <service-bluetooth/messages/SetDeviceName.hpp>
#include <service-desktop/endpoints/bluetooth/BluetoothEventMessages.hpp>
#include <service-desktop/endpoints/bluetooth/BluetoothHelper.hpp>
#include <BtCommand.hpp>
#include <BtKeysStorage.hpp>
#include <service-bluetooth/messages/Unpair.hpp>

#include <typeinfo>

namespace
{
    constexpr auto BluetoothServiceStackDepth = 2560U;
    inline constexpr auto nameSettingsNew     = "ApplicationSettingsNew";
} // namespace

ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth, "", BluetoothServiceStackDepth)


@@ 63,111 67,67 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    worker->run();

    connect(message::bluetooth::RequestBondedDevices(), [&](sys::Message *msg) {
        LOG_INFO("Requested bonded devices!");
        auto bondedDevicesStr =
            std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::BondedDevices));
        auto connectedAddress = std::visit(bluetooth::StringVisitor(),
                                           this->settingsHolder->getValue(bluetooth::Settings::ConnectedDevice));

        return std::make_shared<message::bluetooth::ResponseBondedDevices>(
            SettingsSerializer::fromString(bondedDevicesStr), std::move(connectedAddress));
        auto request = static_cast<message::bluetooth::RequestBondedDevices *>(msg);
        return handle(request);
    });

    connect(message::bluetooth::RequestStatus(), [&](sys::Message *msg) {
        BluetoothStatus status;

        auto state = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State));
        auto visibility =
            std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility));
        status.state      = static_cast<BluetoothStatus::State>(state);
        status.visibility = visibility;

        return std::make_shared<message::bluetooth::ResponseStatus>(status);
        auto request = static_cast<message::bluetooth::RequestStatus *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::SetStatus), [&](sys::Message *msg) {
        auto setStatusMsg = static_cast<message::bluetooth::SetStatus *>(msg);
        auto newBtStatus  = setStatusMsg->getStatus();

        switch (newBtStatus.state) {
        case BluetoothStatus::State::On:
            if (msg->sender == "ServiceDesktop") {
                enabledFromHarness = true;
                LOG_INFO("BT enabled from Harness");
            }
            sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOn));
            break;
        case BluetoothStatus::State::Off:
            sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOff));
            enabledFromHarness = false;
            break;
        default:
            break;
        }
        bluetooth::Command command(newBtStatus.visibility ? bluetooth::Command::Type::VisibilityOn
                                                          : bluetooth::Command::Type::VisibilityOff);
        sendWorkerCommand(command);
        return sys::MessageNone{};
        auto request = static_cast<message::bluetooth::SetStatus *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothPairMessage), [&](sys::Message *msg) {
        auto pairMsg          = static_cast<BluetoothPairMessage *>(msg);
        const auto addrString = pairMsg->addr;
        bd_addr_t addr;
        sscanf_bd_addr(addrString.c_str(), addr);
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, addr));
        return sys::MessageNone{};
        auto request = static_cast<BluetoothPairMessage *>(msg);
        return handle(request);
    });
    connect(typeid(message::bluetooth::Unpair), [&](sys::Message *msg) {
        auto unpairMsg        = static_cast<message::bluetooth::Unpair *>(msg);
        const auto addrString = unpairMsg->getAddr();
        bd_addr_t addr;
        sscanf_bd_addr(addrString.c_str(), addr);
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Unpair, addr));
        return sys::MessageNone{};
        auto request = static_cast<message::bluetooth::Unpair *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::SetDeviceName), [&](sys::Message *msg) {
        auto setNameMsg = static_cast<message::bluetooth::SetDeviceName *>(msg);
        auto newName    = setNameMsg->getName();
        bluetooth::set_name(newName);
        settingsHolder->setValue(bluetooth::Settings::DeviceName, newName);
        return sys::MessageNone{};
        auto request = static_cast<message::bluetooth::SetDeviceName *>(msg);
        return handle(request);
    });

    connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) {
        using namespace sdesktop::developerMode;
        auto req = static_cast<DeveloperModeRequest *>(msg);
        if (typeid(*req->event) == typeid(sdesktop::bluetooth::BluetoothStatusRequestEvent)) {
            auto state   = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State));
            auto event   = std::make_unique<sdesktop::bluetooth::BluetoothStatusRequestEvent>(state);
            auto message = std::make_shared<DeveloperModeRequest>(std::move(event));
            bus.sendUnicast(std::move(message), service::name::service_desktop);
        }

        return sys::MessageNone{};
    connect(typeid(sdesktop::developerMode::DeveloperModeRequest), [&](sys::Message *msg) {
        auto request = static_cast<sdesktop::developerMode::DeveloperModeRequest *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::Connect), [&](sys::Message *msg) {
        auto connectMsg       = static_cast<message::bluetooth::Connect *>(msg);
        const auto addrString = connectMsg->getAddr();
        LOG_DEBUG("Connecting with %s", addrString.c_str());
        bd_addr_t addr;
        sscanf_bd_addr(addrString.c_str(), addr);
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, addr));
        return sys::MessageNone{};
        auto request = static_cast<message::bluetooth::Connect *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::Disconnect), [&](sys::Message *msg) {
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio));
        return sys::MessageNone{};
        auto request = static_cast<message::bluetooth::Disconnect *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothDeviceDisconnectedMessage), [&](sys::Message *msg) {
        settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, std::string());
    connect(typeid(BluetoothMessage), [&](sys::Message *msg) {
        auto request = static_cast<BluetoothMessage *>(msg);
        return handle(request);
    });

    connect(typeid(BluetoothAddrMessage), [&](sys::Message *msg) {
        auto request = static_cast<BluetoothAddrMessage *>(msg);
        return handle(request);
    });

    connect(typeid(message::bluetooth::ConnectResult), [&](sys::Message *msg) {
        auto result = static_cast<message::bluetooth::ConnectResult *>(msg);
        return handle(result);
    });

        sendDevicesAfterDisconnect();
        return sys::MessageNone{};
    connect(typeid(message::bluetooth::DisconnectResult), [&](sys::Message *msg) {
        auto result = static_cast<message::bluetooth::DisconnectResult *>(msg);
        return handle(result);
    });

    settingsHolder->onStateChange = [this]() {


@@ 191,78 151,9 @@ void ServiceBluetooth::ProcessCloseReason(sys::CloseReason closeReason)
    sendCloseReadyMessage(this);
}

sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp)
sys::MessagePointer ServiceBluetooth::DataReceivedHandler([[maybe_unused]] sys::DataMessage *msg,
                                                          [[maybe_unused]] sys::ResponseMessage *resp)
{
    try {
        switch (static_cast<MessageType>(msg->messageType)) {
        case MessageType::BluetoothRequest: {
            BluetoothMessage *lmsg = dynamic_cast<BluetoothMessage *>(msg);
            LOG_INFO("Bluetooth request!");
            switch (lmsg->req) {
            case BluetoothMessage::Start:
                break;
            case BluetoothMessage::Scan:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartScan));
                break;
            case BluetoothMessage::StopScan:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopScan));
                break;
            case BluetoothMessage::PAN: {
                /// TODO request lwip first...
                /// because TODO blocking message - wrecks system
                LOG_INFO("Request LwIP running!");
                //                    auto ret = message_lwip(this, LwIP_message::Request::Start);
                //                    if (ret != sys::ReturnCodes::Success) {
                //                        LOG_ERROR("Request for LwIP start failed");
                //                    }
                //                    else {
                /// TODO request PPP
                LOG_INFO("Start PAN");
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartPan));
                //                    }
            } break;
            case BluetoothMessage::Visible: {
                static bool visibility = true;
                bluetooth::Command command(visibility ? bluetooth::Command::Type::VisibilityOn
                                                      : bluetooth::Command::Type::VisibilityOff);
                sendWorkerCommand(command);
                visibility = !visibility;
            } break;

            case BluetoothMessage::Play:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartStream));
                break;
            case BluetoothMessage::SwitchProfile:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::SwitchProfile));

                break;
            case BluetoothMessage::Disconnect:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio));
                break;
            case BluetoothMessage::Stop:
                sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopStream));
                break;

            default:
                break;
            }
            break;
        }
        case MessageType::BluetoothAddrResult: {
            auto addrMsg = static_cast<BluetoothAddrMessage *>(msg);
            std::string addrString{bd_addr_to_str(addrMsg->addr)};
            LOG_INFO("Connecting with %s", addrString.c_str());
            sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, addrMsg->addr));

        } break;
        default:
            break;
        }
    }
    catch (std::exception &ex) {
        LOG_ERROR("Exception on BtService!: %s", ex.what());
    }

    return std::make_shared<sys::ResponseMessage>();
}



@@ 275,29 166,177 @@ void ServiceBluetooth::sendWorkerCommand(bluetooth::Command command)
{
    xQueueSend(workerQueue, &command, portMAX_DELAY);
}
void ServiceBluetooth::scanStartedCallback()

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestBondedDevices *msg)
    -> std::shared_ptr<sys::Message>
{
    LOG_INFO("Requested bonded devices!");
    auto bondedDevicesStr =
        std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::BondedDevices));
    auto connectedAddress =
        std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::ConnectedDevice));

    return std::make_shared<message::bluetooth::ResponseBondedDevices>(SettingsSerializer::fromString(bondedDevicesStr),
                                                                       std::move(connectedAddress));
}

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestStatus *msg) -> std::shared_ptr<sys::Message>
{
    auto state      = std::visit(bluetooth::IntVisitor(), settingsHolder->getValue(bluetooth::Settings::State));
    auto visibility = std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility));

    BluetoothStatus status{static_cast<BluetoothStatus::State>(state), status.visibility = visibility};
    return std::make_shared<message::bluetooth::ResponseStatus>(status);
}

auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared_ptr<sys::Message>
{
    if (enabledFromHarness) {
        auto event   = std::make_unique<sdesktop::bluetooth::ScanStartedEvent>();
        auto message = std::make_shared<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
        bus.sendUnicast(std::move(message), service::name::service_desktop);
    auto newBtStatus = msg->getStatus();

    switch (newBtStatus.state) {
    case BluetoothStatus::State::On:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOn));
        break;
    case BluetoothStatus::State::Off:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOff));
        break;
    default:
        break;
    }
    bluetooth::Command command(newBtStatus.visibility ? bluetooth::Command::Type::VisibilityOn
                                                      : bluetooth::Command::Type::VisibilityOff);
    sendWorkerCommand(command);
    return sys::MessageNone{};
}
void ServiceBluetooth::scanStoppedCallback()
auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys::Message>
{
    if (enabledFromHarness) {
        auto event   = std::make_unique<sdesktop::bluetooth::ScanStoppedEvent>();
        auto message = std::make_shared<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
        bus.sendUnicast(std::move(message), service::name::service_desktop);
    const auto addrString = msg->addr;
    bd_addr_t addr;
    sscanf_bd_addr(addrString.c_str(), addr);
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, addr));
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::Unpair *msg) -> std::shared_ptr<sys::Message>
{
    const auto addrString = msg->getAddr();
    bd_addr_t addr;
    sscanf_bd_addr(addrString.c_str(), addr);
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Unpair, addr));
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::shared_ptr<sys::Message>
{
    auto newName = msg->getName();
    bluetooth::set_name(newName);
    settingsHolder->setValue(bluetooth::Settings::DeviceName, newName);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::Connect *msg) -> std::shared_ptr<sys::Message>
{
    const auto addrString = msg->getAddr();
    LOG_DEBUG("Connecting with %s", addrString.c_str());
    bd_addr_t addr;
    sscanf_bd_addr(addrString.c_str(), addr);
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, addr));
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::ConnectResult *msg) -> std::shared_ptr<sys::Message>
{
    if (msg->isSucceed()) {
        settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, msg->getAddr());
    }
    bus.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(*msg), nameSettingsNew);
    return sys::MessageNone{};
}

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

void ServiceBluetooth::sendDevicesAfterDisconnect()
auto ServiceBluetooth::handle(message::bluetooth::DisconnectResult *msg) -> std::shared_ptr<sys::Message>
{
    settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, std::string());
    auto bondedDevicesStr =
        std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::BondedDevices));

    bus.sendUnicast(std::make_shared<message::bluetooth::ResponseBondedDevices>(
                        SettingsSerializer::fromString(bondedDevicesStr), std::string()),
                    "ApplicationSettingsNew");
                    nameSettingsNew);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Message>
{
    LOG_INFO("Bluetooth request!");
    switch (msg->req) {
    case BluetoothMessage::Start:
        break;
    case BluetoothMessage::Scan:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartScan));
        break;
    case BluetoothMessage::StopScan:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopScan));
        break;
    case BluetoothMessage::getDevicesAvailable:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::getDevicesAvailable));
        break;
    case BluetoothMessage::PAN: {
        /// TODO request lwip first...
        /// because TODO blocking message - wrecks system
        LOG_INFO("Request LwIP running!");
        //                    auto ret = message_lwip(this, LwIP_message::Request::Start);
        //                    if (ret != sys::ReturnCodes::Success) {
        //                        LOG_ERROR("Request for LwIP start failed");
        //                    }
        //                    else {
        /// TODO request PPP
        LOG_INFO("Start PAN");
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartPan));
        //                    }
    } break;
    case BluetoothMessage::Visible: {
        auto visibility =
            not std::visit(bluetooth::BoolVisitor(), settingsHolder->getValue(bluetooth::Settings::Visibility));
        bluetooth::Command command(visibility ? bluetooth::Command::Type::VisibilityOn
                                              : bluetooth::Command::Type::VisibilityOff);
        sendWorkerCommand(command);
    } break;
    case BluetoothMessage::Play:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StartStream));
        break;
    case BluetoothMessage::SwitchProfile:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::SwitchProfile));
        break;
    case BluetoothMessage::Disconnect:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::DisconnectAudio));
        break;
    case BluetoothMessage::Stop:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::StopStream));
        break;
    default:
        break;
    }

    return std::make_shared<sys::ResponseMessage>();
}

auto ServiceBluetooth::handle(BluetoothAddrMessage *msg) -> std::shared_ptr<sys::Message>
{
    std::string addrString{bd_addr_to_str(msg->addr)};
    LOG_INFO("Connecting with %s", addrString.c_str());
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, msg->addr));
    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(bluetooth::Command::Type::getDevicesAvailable));
    }
    return sys::MessageNone{};
}

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +1 -27
@@ 34,6 34,7 @@ class BluetoothMessage : public sys::DataMessage
        Start,
        Scan,
        StopScan,
        getDevicesAvailable,
        PAN,
        Visible,
        Play,


@@ 47,16 48,6 @@ class BluetoothMessage : public sys::DataMessage
    ~BluetoothMessage() override = default;
};

class BluetoothScanResultMessage : public sys::DataMessage
{
  public:
    std::vector<Devicei> devices;
    BluetoothScanResultMessage(std::vector<Devicei> devices)
        : sys::DataMessage(MessageType::BluetoothScanResult), devices(std::move(devices))
    {}
    ~BluetoothScanResultMessage() override = default;
};

class BluetoothPairResultMessage : public sys::DataMessage
{
  public:


@@ 78,16 69,6 @@ class BluetoothPairResultMessage : public sys::DataMessage
    bool succeed;
};

class BluetoothScanMessage : public sys::DataMessage
{
  public:
    std::vector<Devicei> devices;
    BluetoothScanMessage(std::vector<Devicei> devices)
        : sys::DataMessage(MessageType::BluetoothScanResult), devices(std::move(devices))
    {}
    ~BluetoothScanMessage() override = default;
};

class BluetoothAddrMessage : public sys::DataMessage
{
  public:


@@ 150,10 131,3 @@ class BluetoothRequestStreamResultMessage : public sys::DataMessage
  private:
    std::shared_ptr<BluetoothStreamData> data;
};

class BluetoothDeviceDisconnectedMessage : public sys::DataMessage
{
  public:
    BluetoothDeviceDisconnectedMessage() : DataMessage(MessageType::BluetoothDeviceDisconnected)
    {}
};

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +38 -5
@@ 11,7 11,6 @@
#include <service-db/DBServiceName.hpp>
#include <service-audio/ServiceAudio.hpp>
#include <module-bluetooth/Bluetooth/CommandHandler.hpp>
#include "BluetoothMessage.hpp"
#include "ProfileManager.hpp"
#include <Service/CpuSentinel.hpp>



@@ 23,6 22,30 @@ namespace settings
    class Settings;
}

namespace sdesktop
{
    class Event;
    namespace developerMode
    {
        class DeveloperModeRequest;
        class DeveloperModeMessageWrapper;
    } // namespace developerMode
} // namespace sdesktop

namespace message::bluetooth
{
    class RequestBondedDevices;
    class RequestStatus;
    class SetStatus;
    class Unpair;
    class SetDeviceName;
    class Connect;
    class ConnectResult;
    class Disconnect;
    class DisconnectResult;

} // namespace message::bluetooth

class ServiceBluetooth : public sys::Service
{



@@ 39,14 62,24 @@ class ServiceBluetooth : public sys::Service
    QueueHandle_t workerQueue = nullptr;
    std::shared_ptr<bluetooth::SettingsHolder> settingsHolder;
    bluetooth::ProfileManager *profileManagerPtr = nullptr;
    void scanStartedCallback();
    void scanStoppedCallback();

  private:
    std::unique_ptr<BluetoothWorker> worker;
    std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    bool enabledFromHarness = false;
    void sendDevicesAfterDisconnect();

    [[nodiscard]] auto handle(message::bluetooth::RequestBondedDevices *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::RequestStatus *msg) -> std::shared_ptr<sys::Message>;
    [[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(message::bluetooth::Unpair *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::SetDeviceName *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::Connect *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::ConnectResult *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::Disconnect *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::DisconnectResult *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(BluetoothAddrMessage *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(sdesktop::developerMode::DeveloperModeRequest *msg) -> std::shared_ptr<sys::Message>;
};

namespace sys

M module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp => module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp +3 -0
@@ 9,4 9,7 @@ namespace message::bluetooth
{
    class Disconnect : public BluetoothMessage
    {};

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

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +2 -0
@@ 6,6 6,8 @@ set(SOURCES
    endpoints/backup/BackupRestore.cpp
    endpoints/bluetooth/BluetoothEndpoint.cpp
    endpoints/bluetooth/BluetoothHelper.cpp
    endpoints/bluetooth/BluetoothEventMessages.cpp
    endpoints/bluetooth/BluetoothMessagesHandler.cpp
    endpoints/calllog/CalllogEndpoint.cpp
    endpoints/calllog/CalllogHelper.cpp
    endpoints/contacts/ContactHelper.cpp

M module-services/service-desktop/DesktopMessages.cpp => module-services/service-desktop/DesktopMessages.cpp +1 -21
@@ 3,6 3,7 @@

#include "service-desktop/DesktopMessages.hpp"
#include "parser/MessageHandler.hpp"
#include <module-bluetooth/Bluetooth/Device.hpp>

namespace sdesktop
{


@@ 31,26 32,5 @@ namespace sdesktop
            context.setResponseBody(json11::Json::object{{json::developerMode::cellularStateInfo, stateStr}});
        }
    } // namespace developerMode
    namespace bluetooth
    {
        BluetoothStatusRequestEvent::BluetoothStatusRequestEvent(int state)
        {
            context.setResponseStatus(http::Code::OK);
            context.setEndpoint(EndpointType::bluetooth);
            context.setResponseBody(json11::Json::object{{json::bluetooth::state, state}});
        }
        ScanStartedEvent::ScanStartedEvent()
        {
            context.setResponseStatus(http::Code::OK);
            context.setEndpoint(EndpointType::bluetooth);
            context.setResponseBody(json11::Json::object{{json::bluetooth::scan, json::bluetooth::btOn}});
        }
        ScanStoppedEvent::ScanStoppedEvent()
        {
            context.setResponseStatus(http::Code::OK);
            context.setEndpoint(EndpointType::bluetooth);
            context.setResponseBody(json11::Json::object{{json::bluetooth::scan, json::bluetooth::btOff}});
        }
    } // namespace bluetooth

} // namespace sdesktop

M module-services/service-desktop/README.md => module-services/service-desktop/README.md +7 -7
@@ 1,11 1,11 @@
Service Desktop
=================

This service is handling communication between Mudita Deskatop App and PurePhone.
This service is handling communication between Mudita Desktop App and PurePhone.

**Note:
Service desktop is disabled by default.
To turn it on, please uncomment this line in mail.cpp:**
To turn it on, please uncomment this line in main.cpp:**

`        ret |= sys::SystemManager::CreateService(std::make_shared<ServiceDesktop>(), sysmgr.get());
`


@@ 148,18 148,18 @@ response:

### Service documentation

#### Hi level view
#### High level view

![Flowchart](./doc/how_machine_works.svg)
<img src="./doc/how_machine_works.svg">

#### System asynchronous calls synchronization

Calls from outside world are REST like. This means that:
- for one request 
- there is one response
Calls from the outside world are REST-like. This means that:
- each call contains single request 
- for each call event there is a single response

To provide synchronous response for asynchronous system calls we have special mechanism.
To provide a synchronous response for a asynchronous system call we have special mechanism.

1. Send: `DeveloperModeRequest` message with special `Event` Data
2. Process `DeveloperModeRequest` in system, fill in `Event` Data

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +18 -1
@@ 27,6 27,7 @@
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <module-sys/SystemManager/Constants.hpp>
#include <module-sys/SystemManager/messages/TetheringStateRequest.hpp>
#include <endpoints/bluetooth/BluetoothMessagesHandler.hpp>

#include <purefs/filesystem_paths.hpp>
#include <sys/mount.h>


@@ 58,7 59,9 @@ namespace
    }
} // namespace

ServiceDesktop::ServiceDesktop() : sys::Service(service::name::service_desktop, "", sdesktop::service_stack)
ServiceDesktop::ServiceDesktop()
    : sys::Service(service::name::service_desktop, "", sdesktop::service_stack),
      btMsgHandler(std::make_unique<sdesktop::bluetooth::BluetoothMessagesHandler>(this))
{
    LOG_INFO("[ServiceDesktop] Initializing");



@@ 237,6 240,20 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        return sys::MessageNone{};
    });

    connect(typeid(message::bluetooth::ResponseStatus), [&](sys::Message *msg) {
        auto msgl = static_cast<message::bluetooth::ResponseStatus *>(msg);
        return btMsgHandler->handle(msgl);
    });

    connect(typeid(message::bluetooth::ResponseBondedDevices), [&](sys::Message *msg) {
        auto msgl = static_cast<message::bluetooth::ResponseBondedDevices *>(msg);
        return btMsgHandler->handle(msgl);
    });

    connect(typeid(message::bluetooth::ResponseVisibleDevices), [&](sys::Message *msg) {
        auto msgl = static_cast<message::bluetooth::ResponseVisibleDevices *>(msg);
        return btMsgHandler->handle(msgl);
    });
    settings->registerValueChange(updateos::settings::history,
                                  [this](const std::string &value) { updateOS->setInitialHistory(value); });


M module-services/service-desktop/endpoints/bluetooth/BluetoothEndpoint.cpp => module-services/service-desktop/endpoints/bluetooth/BluetoothEndpoint.cpp +3 -1
@@ 1,6 1,6 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "BluetoothHelper.hpp"

#include "BluetoothEndpoint.hpp"
#include <endpoints/Context.hpp>



@@ 13,11 13,13 @@ auto BluetoothEndpoint::handle(Context &context) -> void
        helper->processGetRequest(context);
        break;
    case http::Method::post:
        helper->processPostRequest(context);
        break;
    case http::Method::put:
        helper->processPutRequest(context);
        break;
    case http::Method::del:
        helper->processDeleteRequest(context);
        break;
    }
}

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

#include "BluetoothEventMessages.hpp"

namespace sdesktop::bluetooth
{
    using namespace parserFSM;

    BluetoothEvent::BluetoothEvent()
    {
        context.setResponseStatus(http::Code::OK);
        context.setEndpoint(EndpointType::bluetooth);
    }

} // namespace sdesktop::bluetooth

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

#pragma once

#include <service-desktop/DesktopEvent.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>

namespace sdesktop::bluetooth
{
    class BluetoothEvent : public Event
    {
      public:
        BluetoothEvent();
    };

    class GetAvailableDevicesEvent : public BluetoothEvent
    {
      public:
        GetAvailableDevicesEvent() = default;
    };

} // namespace sdesktop::bluetooth

M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp => module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp +86 -19
@@ 2,52 2,119 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "BluetoothHelper.hpp"
#include <service-desktop/DesktopMessages.hpp>
#include "BluetoothEventMessages.hpp"
#include <service-bluetooth/BluetoothMessage.hpp>
#include <parser/MessageHandler.hpp>
#include <service-bluetooth/messages/SetStatus.hpp>
#include <service-desktop/DeveloperModeMessage.hpp>

#include <service-bluetooth/service-bluetooth/messages/Connect.hpp>
#include <service-bluetooth/service-bluetooth/messages/Disconnect.hpp>
#include <service-bluetooth/service-bluetooth/messages/Status.hpp>
#include <service-bluetooth/service-bluetooth/messages/BondedDevices.hpp>
#include <service-bluetooth/service-bluetooth/messages/Unpair.hpp>

namespace btConsts = parserFSM::json::bluetooth;

auto parserFSM::BluetoothHelper::processPutRequest(parserFSM::Context &context) -> sys::ReturnCodes
{
    auto body = context.getBody();
    if (auto command = body[json::bluetooth::command].string_value(); !command.empty()) {
        BluetoothMessage::Request btRequest{};

        if (command == json::bluetooth::scanOn) {
            btRequest = BluetoothMessage::Request::Scan;
    std::unique_ptr<sys::Message> msg;
    if (auto command = body[btConsts::command].string_value(); !command.empty()) {
        if (command == btConsts::commands::scanOn) {
            msg = std::make_unique<BluetoothMessage>(BluetoothMessage::Request::Scan);
        }
        else if (command == btConsts::commands::scanOff) {
            msg = std::make_unique<BluetoothMessage>(BluetoothMessage::Request::StopScan);
        }
        else if (command == json::bluetooth::scanOff) {
            btRequest = BluetoothMessage::Request::StopScan;
        else if (command == btConsts::commands::changeVisibility) {
            msg = std::make_unique<BluetoothMessage>(BluetoothMessage::Request::Visible);
        }
        ownerServicePtr->bus.sendUnicast(std::make_shared<BluetoothMessage>(btRequest), "ServiceBluetooth");
    }
    else if (auto state = body[json::bluetooth::state].string_value(); !state.empty()) {
    else if (auto state = body[btConsts::state].object_items(); !state.empty()) {
        BluetoothStatus status{};
        if (state == json::bluetooth::btOn) {
        const auto &power = state[btConsts::states::power];
        if (power == json::bluetooth::on) {
            status.state = BluetoothStatus::State::On;
            LOG_INFO("turning on BT from harness!");
        }
        else if (state == json::bluetooth::btOff) {
        else if (power == json::bluetooth::off) {
            status.state = BluetoothStatus::State::Off;
            LOG_INFO("turning off BT from harness!");
        }
        ::message::bluetooth::SetStatus setStatus(status);
        const auto &visibility = state[btConsts::states::visibility];
        status.visibility      = visibility == json::bluetooth::on;

        ownerServicePtr->bus.sendUnicast(std::make_shared<::message::bluetooth::SetStatus>(std::move(setStatus)),
                                         "ServiceBluetooth");
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        ::message::bluetooth::SetStatus setStatus(status);
        msg = std::make_unique<::message::bluetooth::SetStatus>(std::move(setStatus));
    }

    sendRequest(context, std::move(msg));
    MessageHandler::putToSendQueue(context.createSimpleResponse());
    return sys::ReturnCodes::Unresolved;
}
auto parserFSM::BluetoothHelper::processGetRequest(parserFSM::Context &context) -> sys::ReturnCodes
{
    auto body = context.getBody();
    std::unique_ptr<sys::Message> msg;
    if (body[json::bluetooth::state].bool_value()) {
        msg = std::make_unique<::message::bluetooth::RequestStatus>();
    }
    else if (auto devices = body[json::bluetooth::devices].string_value(); !devices.empty()) {
        if (devices == json::bluetooth::devicesValues::scanned) {
            auto event = std::make_unique<sdesktop::bluetooth::GetAvailableDevicesEvent>();
            msg        = std::make_unique<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
        }
        else if (devices == json::bluetooth::devicesValues::bonded) {
            msg = std::make_unique<::message::bluetooth::RequestBondedDevices>();
        }
    }

        auto event = std::make_unique<sdesktop::bluetooth::BluetoothStatusRequestEvent>();
        auto msg   = std::make_shared<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
        ownerServicePtr->bus.sendUnicast(std::move(msg), "ServiceBluetooth");
    sendRequest(context, std::move(msg));
    return sys::ReturnCodes::Unresolved;
}

auto parserFSM::BluetoothHelper::processPostRequest(Context &context) -> sys::ReturnCodes
{
    auto body = context.getBody();
    std::unique_ptr<sys::Message> msg;
    if (auto address = body[json::bluetooth::connect].string_value(); !address.empty()) {
        msg = std::make_unique<::message::bluetooth::Connect>(std::move(address));
    }
    else if (auto address = body[json::bluetooth::pair].string_value(); !address.empty()) {
        LOG_INFO("Requesting pairing with %s form harness", address.c_str());
        msg = std::make_unique<BluetoothPairMessage>(std::move(address));
    }

    sendRequest(context, std::move(msg));
    MessageHandler::putToSendQueue(context.createSimpleResponse());
    return sys::ReturnCodes::Unresolved;
}

auto parserFSM::BluetoothHelper::processDeleteRequest(Context &context) -> sys::ReturnCodes
{
    auto body = context.getBody();
    std::unique_ptr<sys::Message> msg;
    if (auto command = body[json::bluetooth::command].string_value(); !command.empty()) {
        if (command == json::bluetooth::commands::disconnect) {
            msg = std::make_unique<::message::bluetooth::Disconnect>();
        }
    }
    else if (auto address = body[json::bluetooth::unpair].string_value(); !address.empty()) {
        LOG_INFO("Requesting pairing with %s form harness", address.c_str());
        msg = std::make_unique<::message::bluetooth::Unpair>(std::move(address));
    }
    sendRequest(context, std::move(msg));
    MessageHandler::putToSendQueue(context.createSimpleResponse());
    return sys::ReturnCodes::Unresolved;
}
void parserFSM::BluetoothHelper::sendRequest(parserFSM::Context &context, std::unique_ptr<sys::Message> msg)
{
    if (msg != nullptr) {
        ownerServicePtr->bus.sendUnicast(std::move(msg), "ServiceBluetooth");
    }
    else {
        LOG_ERROR("No valid request created, returning simpleResponse ...");
        MessageHandler::putToSendQueue(context.createSimpleResponse());
    }
}

M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.hpp => module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.hpp +33 -6
@@ 3,7 3,6 @@
#pragma once

#include <endpoints/Context.hpp>

#include <Common/Query.hpp>
#include <Service/Common.hpp>
#include <Service/Service.hpp>


@@ 19,21 18,49 @@ namespace parserFSM
    class BluetoothHelper
    {
        sys::Service *ownerServicePtr = nullptr;
        void sendRequest(parserFSM::Context &context, std::unique_ptr<sys::Message> msg);

      public:
        BluetoothHelper(sys::Service *_ownerServicePtr) : ownerServicePtr(_ownerServicePtr){};
        auto processPutRequest(Context &context) -> sys::ReturnCodes;
        auto processGetRequest(Context &context) -> sys::ReturnCodes;
        auto processPostRequest(Context &context) -> sys::ReturnCodes;
        auto processDeleteRequest(Context &context) -> sys::ReturnCodes;
    };

    namespace json::bluetooth
    {
        inline constexpr auto state   = "state";
        inline constexpr auto btOn    = "on";
        inline constexpr auto btOff   = "off";
        inline constexpr auto scanOn  = "scanOn";
        inline constexpr auto scanOff = "scanOff";
        inline constexpr auto scan    = "scan";
        namespace states
        {
            inline constexpr auto power      = "power";
            inline constexpr auto visibility = "visibility";
        } // namespace states
        inline constexpr auto on   = "on";
        inline constexpr auto off  = "off";
        inline constexpr auto scan = "scan";

        inline constexpr auto command = "command";
        namespace commands
        {
            inline constexpr auto scanOn           = "scanOn";
            inline constexpr auto scanOff          = "scanOff";
            inline constexpr auto disconnect       = "disconnect";
            inline constexpr auto changeVisibility = "changeVisibility";
        } // namespace commands
        inline constexpr auto success = "success";
        inline constexpr auto failure = "failure";

        inline constexpr auto pair    = "pair";
        inline constexpr auto unpair  = "unpair";
        inline constexpr auto connect = "connect";
        inline constexpr auto devices = "devices";
        namespace devicesValues
        {
            inline constexpr auto address = "address";
            inline constexpr auto name    = "name";
            inline constexpr auto scanned = "scanned";
            inline constexpr auto bonded  = "bonded";
        } // namespace devicesValues
    } // namespace json::bluetooth
} // namespace parserFSM

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

#include "BluetoothMessagesHandler.hpp"
#include "BluetoothEventMessages.hpp"
#include "BluetoothHelper.hpp"
#include <service-desktop/ServiceDesktop.hpp>
#include <service-desktop/DeveloperModeMessage.hpp>
#include <btstack_util.h>

namespace sdesktop::bluetooth
{
    class ResponseEvent : public sdesktop::bluetooth::BluetoothEvent
    {
      public:
        ResponseEvent(json11::Json::object responseBody)
        {
            context.setResponseBody(std::move(responseBody));
        }
    };

} // namespace sdesktop::bluetooth
using namespace sdesktop::bluetooth;
namespace btConstants = parserFSM::json::bluetooth;

namespace
{
    json11::Json::object device2json(const Devicei &device)
    {
        LOG_DEBUG("Device: name=%s, address=%s", device.name.c_str(), bd_addr_to_str(device.address));
        return json11::Json::object{{btConstants::devicesValues::address, std::string(bd_addr_to_str(device.address))},
                                    {btConstants::devicesValues::name, device.name}};
    }
    json11::Json::array devices2json(const std::vector<Devicei> &devices)
    {
        json11::Json::array serialized;
        serialized.reserve(devices.size());
        for (const auto &device : devices) {
            serialized.emplace_back(device2json(device));
        }
        return serialized;
    }
} // namespace

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

auto BluetoothMessagesHandler::handle(message::bluetooth::ResponseStatus *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    auto status = msg->getStatus();
    json11::Json::object responseBody(
        {{btConstants::state,
          json11::Json::object{
              {btConstants::states::power,
               status.state == BluetoothStatus::State::On ? btConstants::on : btConstants::off},
              {btConstants::states::visibility, status.visibility ? btConstants::on : btConstants::off}}}});
    send(std::make_unique<ResponseEvent>(std::move(responseBody)));
    return std::make_shared<sys::ResponseMessage>();
}

auto BluetoothMessagesHandler::handle(message::bluetooth::ResponseBondedDevices *msg)
    -> std::shared_ptr<sys::ResponseMessage>
{
    LOG_DEBUG("Currently connected device address=%s", msg->getAddressOfConnectedDevice().c_str());
    json11::Json::object responseBody({{btConstants::devices, devices2json(msg->getDevices())},
                                       {btConstants::devicesValues::address, msg->getAddressOfConnectedDevice()}});
    send(std::make_unique<ResponseEvent>(std::move(responseBody)));
    return std::make_shared<sys::ResponseMessage>();
}
auto BluetoothMessagesHandler::handle(message::bluetooth::ResponseVisibleDevices *msg)
    -> std::shared_ptr<sys::ResponseMessage>
{
    json11::Json::object responseBody({{btConstants::devices, devices2json(msg->getDevices())}});
    send(std::make_unique<ResponseEvent>(std::move(responseBody)));
    return std::make_shared<sys::ResponseMessage>();
}

void BluetoothMessagesHandler::send(std::unique_ptr<ResponseEvent> event)
{
    auto message = std::make_shared<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
    ownerService->bus.sendUnicast(std::move(message), service::name::service_desktop);
}

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

#pragma once
#include <memory>
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-bluetooth/messages/BondedDevices.hpp>
#include <service-bluetooth/messages/Status.hpp>

namespace sys
{
    class Service;
}

namespace sdesktop::developerMode
{
    class DeveloperModeRequest;
}

namespace sdesktop::bluetooth
{
    class ResponseEvent;
    class BluetoothMessagesHandler
    {
        sys::Service *ownerService = nullptr;

        void send(std::unique_ptr<ResponseEvent> event);

      public:
        BluetoothMessagesHandler(sys::Service *ownerService);

        [[nodiscard]] auto handle(message::bluetooth::ResponseStatus *msg) -> std::shared_ptr<sys::ResponseMessage>;
        [[nodiscard]] auto handle(message::bluetooth::ResponseBondedDevices *msg)
            -> std::shared_ptr<sys::ResponseMessage>;
        [[nodiscard]] auto handle(message::bluetooth::ResponseVisibleDevices *msg)
            -> std::shared_ptr<sys::ResponseMessage>;
    };
} // namespace sdesktop::bluetooth

M module-services/service-desktop/service-desktop/DesktopMessages.hpp => module-services/service-desktop/service-desktop/DesktopMessages.hpp +0 -21
@@ 4,7 4,6 @@
#pragma once

#include <endpoints/developerMode/DeveloperModeEndpoint.hpp>
#include <endpoints/bluetooth/BluetoothEndpoint.hpp>
#include <endpoints/update/UpdateMuditaOS.hpp>

#include <service-appmgr/Actions.hpp>


@@ 198,26 197,6 @@ namespace sdesktop
        };
    } // namespace developerMode

    namespace bluetooth
    {
        class BluetoothStatusRequestEvent : public Event
        {
          public:
            BluetoothStatusRequestEvent() = default;
            explicit BluetoothStatusRequestEvent(int state);
        };
        class ScanStartedEvent : public Event
        {
          public:
            ScanStartedEvent();
        };
        class ScanStoppedEvent : public Event
        {
          public:
            ScanStoppedEvent();
        };

    } // namespace bluetooth

    namespace transfer
    {

M module-services/service-desktop/service-desktop/DeveloperModeMessage.hpp => module-services/service-desktop/service-desktop/DeveloperModeMessage.hpp +0 -1
@@ 14,6 14,5 @@ namespace sdesktop::developerMode
        std::unique_ptr<sdesktop::Event> event;
        explicit DeveloperModeRequest(std::unique_ptr<sdesktop::Event> event);
        DeveloperModeRequest();
        ~DeveloperModeRequest() override = default;
    };
} // namespace sdesktop::developerMode

M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +6 -0
@@ 35,6 35,11 @@ namespace sdesktop

}; // namespace sdesktop

namespace sdesktop::bluetooth
{
    class BluetoothMessagesHandler;
}

class ServiceDesktop : public sys::Service
{
  public:


@@ 83,6 88,7 @@ class ServiceDesktop : public sys::Service
    std::unique_ptr<sdesktop::USBSecurityModel> usbSecurityModel;
    std::unique_ptr<settings::Settings> settings;
    std::unique_ptr<sys::Timer> transferTimer;
    std::unique_ptr<sdesktop::bluetooth::BluetoothMessagesHandler> btMsgHandler;
};

namespace sys

M source/MessageType.hpp => source/MessageType.hpp +1 -0
@@ 215,6 215,7 @@ enum class MessageType
    Restore,
    Factory,
    DeveloperModeRequest,
    DeveloperModeMessageWrapper,
    PasscodeRequest,
    TransferTimer,


A test/pytest/service-bluetooth/bt_fixtures.py => test/pytest/service-bluetooth/bt_fixtures.py +65 -0
@@ 0,0 1,65 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import pytest
from harness import log
from harness.interface.defs import key_codes, status
from harness.dom_parser_utils import *
import time
from bt_utils import *

@pytest.fixture(scope='function')
def bt_main_window(harness):
    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'MainWindow' )
    harness.connection.send_key_code(key_codes["enter"])

    log.info("Navigating to ApplicationSettings")
    harness.open_application("settings")
    if harness.connection.get_application_name() != "ApplicationSettingsNew":
        time.sleep(5)
        assert harness.connection.get_application_name() == "ApplicationSettingsNew"

    log.info("Opening Bluetooth")
    harness.connection.send_key_code(key_codes["down"])
    harness.connection.send_key_code(key_codes["enter"])

@pytest.fixture(scope='function')
def bt_reset(harness):
    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'Bluetooth' )

    parent_of_list_items = find_parent(current_window_content, 'ListItem')
    if item_has_child_that_contains_recursively( parent_of_list_items, [('TextValue','Bluetooth'), ('TextValue', 'ON')] ) :
        log.info("Bluetooth is ON, turing OFF...")
        harness.connection.send_key_code(key_codes["enter"])

    current_window_content = get_window_content(harness, 1)
    parent_of_list_items = find_parent(current_window_content, 'ListItem')
    assert item_has_child_that_contains_recursively( parent_of_list_items, [('TextValue','Bluetooth'), ('TextValue', 'OFF')] )

    log.info("Turing Bluetooth ON...")
    harness.connection.send_key_code(key_codes["enter"])

@pytest.fixture(scope='function')
def bt_all_devices(harness):
    log.info("Navigating to AllDevices window...")
    time.sleep(1)
    harness.connection.send_key_code(key_codes["down"])
    harness.connection.send_key_code(key_codes["enter"])

    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'AllDevices')

@pytest.fixture(scope='function')
def bt_state_on(harness):
    status = bt_get_state(harness)
    if status["power"] == "off":
        bt_set_status(harness, "on")


@pytest.fixture(scope='function')
def bt_state_off(harness):
    status = bt_get_state(harness)
    if status["power"] == "on":
        bt_set_status(harness, "off")

M test/pytest/service-bluetooth/bt_utils.py => test/pytest/service-bluetooth/bt_utils.py +90 -43
@@ 1,48 1,95 @@
import pytest
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

from harness.interface.defs import status
from harness import log
from harness.interface.defs import key_codes
from harness.dom_parser_utils import *
import time

@pytest.fixture(scope='function')
def bt_main_window(harness):
    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'MainWindow' )
    harness.connection.send_key_code(key_codes["enter"])

    log.info("Navigating to ApplicationSettings")
    harness.open_application("settings")
    if harness.connection.get_application_name() != "ApplicationSettingsNew":
        time.sleep(5)
        assert harness.connection.get_application_name() == "ApplicationSettingsNew"

    log.info("Opening Bluetooth")
    harness.connection.send_key_code(key_codes["down"])
    harness.connection.send_key_code(key_codes["enter"])

@pytest.fixture(scope='function')
def bt_reset(harness):
    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'Bluetooth' )

    parent_of_list_items = find_parent(current_window_content, 'ListItem')
    if item_has_child_that_contains_recursively( parent_of_list_items, [('TextValue','Bluetooth'), ('TextValue', 'ON')] ) :
        log.info("Bluetooth is ON, turing OFF...")
        harness.connection.send_key_code(key_codes["enter"])

    current_window_content = get_window_content(harness, 1)
    parent_of_list_items = find_parent(current_window_content, 'ListItem')
    assert item_has_child_that_contains_recursively( parent_of_list_items, [('TextValue','Bluetooth'), ('TextValue', 'OFF')] )

    log.info("Turing Bluetooth ON...")
    harness.connection.send_key_code(key_codes["enter"])

@pytest.fixture(scope='function')
def bt_all_devices(harness):
    log.info("Navigating to AllDevices window...")
def bt_get_state(harness):
    time.sleep(1)
    harness.connection.send_key_code(key_codes["down"])
    harness.connection.send_key_code(key_codes["enter"])
    body = {"state": True}
    ret = harness.endpoint_request("bluetooth", "get", body)
    assert ret["status"] == status["OK"]
    return ret["body"]["state"]

def bt_set_status(harness, power, visibility = "off"):
    log.info("Turning Bluetooth {} with visibility {}...".format(power, visibility))
    body = {"state": {"power" : power , "visibility" : visibility }}
    ret = harness.endpoint_request("bluetooth", "put", body)
    assert ret["status"] == status["OK"]

def bt_command(harness, command,  http_method = "put"):
    log.info("Sending command: {} with http_method={}".format(command, http_method))
    body = {"command": command}
    ret = harness.endpoint_request("bluetooth", http_method, body)
    assert ret["status"] == status["OK"]

def bt_pair_command(harness, pair_command, address, http_method):
    log.info("Requesting {}ing with address={}...".format(pair_command, address))
    body = {pair_command: address}
    ret = harness.endpoint_request("bluetooth", http_method, body)
    assert ret["status"] == status["OK"]

def bt_get_device_by_name(devices, name) -> dict :
    for device in devices:
        if device["name"] == name :
            return device
    return {}

def bt_find_device(harness, device_origin, device_name, max_attempts = 7):
    log.info("Getting {} devices".format(device_origin))
    body = {"devices": device_origin}

    for i in range(max_attempts):
        ret = harness.endpoint_request("bluetooth", "get", body)
        device = bt_get_device_by_name(ret["body"]["devices"], device_name)
        if device:
            log.info("Found {}, address={}".format(device_name, device.get('address')))
            return device
        if i != max_attempts - 1:
            log.info("Device {} not found, retrying...".format(device_name))
            time.sleep(2)
    return {}

def bt_is_device_forgotten(harness, device_name, max_attempts = 7):
    log.info("Checking if pair forgetting succeeded...")
    body = {"devices": "bonded"}

    for i in range(max_attempts):
        ret = harness.endpoint_request("bluetooth", "get", body)
        device = bt_get_device_by_name(ret["body"]["devices"], device_name)
        if not device:
            return True
        if i != max_attempts - 1:
            log.info("Device {} still paired, retrying...".format(device_name))
            time.sleep(2)
    return False

def bt_get_connected_address(harness, max_attempts = 10):
    log.info("Getting address of connected device")
    body = {"devices": "bonded"}

    for i in range(max_attempts):
        ret = harness.endpoint_request("bluetooth", "get", body)
        address = ret["body"]["address"]
        if len(address) > 0:
            log.info("Device connected={}".format(address))
            return address
        if i != max_attempts - 1:
            log.info("No device connected, retrying...")
            time.sleep(2)
    return ""

def bt_is_device_disconnected(harness, max_attempts = 7):
    log.info("Checking if disconnecting succeeded...")
    body = {"devices": "bonded"}

    current_window_content = get_window_content(harness, 1)
    assert item_contains_recursively(current_window_content, 'WindowName', 'AllDevices')
    for i in range(max_attempts):
        ret = harness.endpoint_request("bluetooth", "get", body)
        address = ret["body"]["address"]
        if len(address) == 0:
            return True
        if i != max_attempts - 1:
            log.info("Device {} connected, retrying...".format(address))
            time.sleep(2)
    return False

A test/pytest/service-bluetooth/test_basic_control_dev_perspective.py => test/pytest/service-bluetooth/test_basic_control_dev_perspective.py +55 -0
@@ 0,0 1,55 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import time
import pytest
from harness import log
from harness.dom_parser_utils import *
from bt_fixtures import *
from bt_utils import *

@pytest.mark.rt1051
@pytest.mark.usefixtures("bt_state_off")
@pytest.mark.usefixtures("usb_unlocked")
def test_bt_turning_on_off(harness):
    state = bt_get_state(harness)
    assert state["power"] == "off"
    log.info("BT turned off.")

    bt_set_status(harness, "on")

    state = bt_get_state(harness)
    assert state["power"] == "on"
    log.info("BT turned on successfully")

    bt_set_status(harness, "off")

    state = bt_get_state(harness)
    assert state["power"] == "off"
    log.info("BT turned off successfully")

@pytest.mark.rt1051
@pytest.mark.usefixtures("bt_state_on")
@pytest.mark.usefixtures("usb_unlocked")
def test_bt_visibility_on_off(harness):
    state = bt_get_state(harness)
    assert state["power"] == "on"

    visibility = state["visibility"]
    log.info("Device visibility is {}".format(visibility))
    time.sleep(1)
    bt_command(harness, "changeVisibility")

    time.sleep(1)
    state = bt_get_state(harness)
    assert (visibility != state["visibility"])
    visibility = state["visibility"]
    log.info("Device visibility is {}".format(visibility))

    time.sleep(1)
    bt_command(harness, "changeVisibility")

    time.sleep(1)
    state = bt_get_state(harness)
    assert (visibility != state["visibility"])


A test/pytest/service-bluetooth/test_pairing_dev_perspective.py => test/pytest/service-bluetooth/test_pairing_dev_perspective.py +80 -0
@@ 0,0 1,80 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import time
import pytest
from harness import log
from bt_fixtures import *
from bt_utils import *

@pytest.mark.rt1051
@pytest.mark.usefixtures("bt_state_on")
@pytest.mark.usefixtures("usb_unlocked")
@pytest.mark.skipif("not config.getvalue('--bt_device')", reason='--bt_device was not specified')
def test_bt_pairing_dev(harness, bt_device):
    time.sleep(1)
    bt_command(harness, "scanOn")

    time.sleep(1)
    device = bt_find_device(harness, "scanned", bt_device)
    assert device
    address = device.get('address')

    bt_pair_command(harness, "pair", address, "post")
    time.sleep(1)
    bt_command(harness, "scanOff")

    time.sleep(1)
    device = bt_find_device(harness, "bonded", bt_device)
    assert device
    assert device.get('address') == address

    bt_set_status(harness, "off")
    time.sleep(1)
    bt_set_status(harness, "on")
    time.sleep(1)

    time.sleep(1)
    log.info("Checking if {} is still paired after turning BT off-on...".format(bt_device))
    device = bt_find_device(harness, "bonded", bt_device)
    assert device
    assert device.get('address') == address

    bt_pair_command(harness, "unpair", address, "del")
    assert bt_is_device_forgotten(harness, bt_device)
    log.info("Device {} successfully unpaired.".format(address))


@pytest.mark.rt1051
@pytest.mark.usefixtures("bt_state_on")
@pytest.mark.usefixtures("usb_unlocked")
@pytest.mark.skipif("not config.getvalue('--bt_device')", reason='--bt_device was not specified')
def test_bt_connection_dev(harness, bt_device):
    time.sleep(1)
    bt_command(harness, "scanOn")

    time.sleep(1)
    device = bt_find_device(harness, "scanned", bt_device)
    assert device
    address = device.get('address')

    bt_pair_command(harness, "pair", address, "post")
    time.sleep(1)
    bt_command(harness, "scanOff")

    time.sleep(1)
    device = bt_find_device(harness, "bonded", bt_device)
    assert device
    assert device.get('address') == address

    bt_pair_command(harness, "connect", address, "post")
    time.sleep(1)
    connected_address = bt_get_connected_address(harness)
    assert connected_address == address

    bt_command(harness, "disconnect", "del")
    assert bt_is_device_disconnected(harness)

    bt_pair_command(harness, "unpair", address, "del")
    assert bt_is_device_forgotten(harness, bt_device)
    log.info("Device {} successfully unpaired.".format(address))

R test/pytest/service-bluetooth/test_pairing.py => test/pytest/service-bluetooth/test_pairing_hmi_perspective.py +5 -2
@@ 1,9 1,12 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import time
import pytest
from harness import log
from harness.dom_parser_utils import *
from harness.interface.defs import key_codes
from bt_utils import *
from bt_fixtures import *

@pytest.mark.rt1051
@pytest.mark.usefixtures("bt_all_devices")


@@ 12,7 15,7 @@ from bt_utils import *
@pytest.mark.usefixtures("phone_in_desktop")
@pytest.mark.usefixtures("phone_unlocked")
@pytest.mark.skipif("not config.getvalue('--bt_device')", reason='--bt_device was not specified')
def test_bt_pairing(harness, bt_device):
def test_bt_pairing_hmi(harness, bt_device):
    if not bt_device:
        return
    bt_device_name = bt_device

D test/pytest/service-desktop/test_bluetooth.py => test/pytest/service-desktop/test_bluetooth.py +0 -75
@@ 1,75 0,0 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import pytest
from harness.interface.defs import status
from harness import log
import time


@pytest.mark.rt1051
@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_bluetooth_on_off(harness):
    body = {"state": True}
    ret = harness.endpoint_request("bluetooth", "get", body)
    print(ret)
    assert ret["status"] == status["OK"]
    if ret["body"]["state"] == 0:
        log.info("BT turned off, turning on...")
        body = {"state": "on"}
        ret = harness.endpoint_request("bluetooth", "put", body)

        time.sleep(5)
        body = {"state": True}
        ret = harness.endpoint_request("bluetooth", "get", body)

        assert ret["status"] == status["OK"]

    assert ret["body"]["state"] == 1

    log.info("BT turning off...")
    body = {"state": "off"}
    ret = harness.endpoint_request("bluetooth", "put", body)

    body = {"state": True}
    ret = harness.endpoint_request("bluetooth", "get", body)
    assert ret["body"]["state"] == 0


@pytest.mark.rt1051
@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_bluetooth_scan(harness):
    body = {"state": True}
    ret = harness.endpoint_request("bluetooth", "get", body)
    print(ret)
    assert ret["status"] == status["OK"]
    if ret["body"]["state"] == 0:
        log.info("BT turned off, turning on...")
        body = {"state": "on"}
        ret = harness.endpoint_request("bluetooth", "put", body)

        time.sleep(5)
        body = {"state": True}
        ret = harness.endpoint_request("bluetooth", "get", body)

        assert ret["status"] == status["OK"]

    assert ret["body"]["state"] == 1

    body = {"command": "scanOn"}
    ret = harness.endpoint_request("bluetooth", "put", body)
    assert ret["body"]["scan"] == "on"

    time.sleep(1)
    body = {"command": "scanOff"}
    ret = harness.endpoint_request("bluetooth", "put", body)
    assert ret["body"]["scan"] == "off"

    body = {"state": "off"}
    ret = harness.endpoint_request("bluetooth", "put", body)

    body = {"state": True}
    ret = harness.endpoint_request("bluetooth", "get", body)
    assert ret["body"]["state"] == 0