~aleteoryx/muditaos

8145a88401da261bc6abcdf7b2bb60e9adc8e1c3 — Bartosz Cichocki 4 years ago 2ae306b
[EGD-7077] Rework of BT connection status in AppSettings

Reworked AppSettings handling of BT devices to be prepared for
handling the HFP profile, eliminating by the way few bugs and
speeding up the flow (by getting rid of few refreshes).

Added unit tests for handling the BT devices flow
56 files changed, 1182 insertions(+), 338 deletions(-)

M module-apps/application-settings/ApplicationSettings.cpp
M module-apps/application-settings/CMakeLists.txt
M module-apps/application-settings/include/application-settings/ApplicationSettings.hpp
M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp
M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.hpp
M module-apps/application-settings/windows/bluetooth/AddDeviceWindow.cpp
M module-apps/application-settings/windows/bluetooth/AddDeviceWindow.hpp
M module-apps/application-settings/windows/bluetooth/AllDevicesWindow.cpp
M module-apps/application-settings/windows/bluetooth/AllDevicesWindow.hpp
M module-apps/application-settings/windows/bluetooth/PhoneNameWindow.hpp
M module-apps/tests/CMakeLists.txt
A module-apps/tests/tests-BluetoothSettingsModel.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/CommandHandler.cpp
M module-bluetooth/Bluetooth/CommandHandler.hpp
M module-bluetooth/Bluetooth/Device.hpp
M module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp
M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp
M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp
M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp
M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp
M module-bluetooth/CMakeLists.txt
M module-bluetooth/tests/CMakeLists.txt
A module-bluetooth/tests/tests-BluetoothDevicesModel.cpp
M module-bluetooth/tests/tests-StatefulController.cpp
M module-services/service-bluetooth/CMakeLists.txt
M module-services/service-bluetooth/ServiceBluetooth.cpp
A module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.cpp
A module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.hpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
A module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.cpp
A module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
M module-services/service-bluetooth/service-bluetooth/SettingsSerializer.cpp
M module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp
M module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp
M module-services/service-bluetooth/service-bluetooth/messages/Status.hpp
A module-services/service-bluetooth/service-bluetooth/messages/SyncDevices.hpp
M module-services/service-bluetooth/service-bluetooth/messages/Unpair.hpp
M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp
M module-services/service-evtmgr/EventManager.cpp
M module-sys/Service/Common.hpp
M products/PurePhone/services/appmgr/ApplicationManager.cpp
M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +40 -22
@@ 73,10 73,10 @@
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-bluetooth/messages/Status.hpp>
#include <service-bluetooth/messages/Unpair.hpp>
#include <service-db/agents/settings/SystemSettings.hpp>
#include <service-bluetooth/messages/Disconnect.hpp>
#include <service-db/Settings.hpp>
#include <service-db/agents/settings/SystemSettings.hpp>
#include <module-services/service-appmgr/include/service-appmgr/Constants.hpp>
#include <module-services/service-db/agents/settings/SystemSettings.hpp>
#include <module-services/service-evtmgr/service-evtmgr/ScreenLightControlMessage.hpp>
#include <module-services/service-evtmgr/service-evtmgr/Constants.hpp>
#include <module-services/service-evtmgr/service-evtmgr/EVMessages.hpp>


@@ 86,6 86,7 @@
#include <apps-common/windows/Dialog.hpp>

#include <i18n/i18n.hpp>
#include <service-bluetooth/messages/SyncDevices.hpp>

namespace app
{


@@ 106,6 107,7 @@ namespace app
          AsyncCallbackReceiver{this}, soundsPlayer{std::make_shared<SoundsPlayer>(this)}
    {
        bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);
        bus.channels.push_back(sys::BusChannel::BluetoothNotifications);

        CellularServiceAPI::SubscribeForOwnNumber(this, [&](const std::string &number) {
            selectedSimNumber = number;


@@ 115,6 117,7 @@ namespace app
            Store::GSM::get()->sim == selectedSim) {
            CellularServiceAPI::RequestForOwnNumber(this);
        }
        bluetoothSettingsModel = std::make_shared<BluetoothSettingsModel>(this);
    }

    ApplicationSettings::~ApplicationSettings()


@@ 179,12 182,21 @@ namespace app
            return sys::MessageNone{};
        });

        connect(typeid(::message::bluetooth::SyncDevices), [&](sys::Message *msg) {
            auto message = static_cast<::message::bluetooth::SyncDevices *>(msg);
            bluetoothSettingsModel->replaceDevicesList(message->getDevices());

            if (gui::window::name::all_devices == getCurrentWindow()->getName()) {
                switchWindow(gui::window::name::all_devices);
            }
            return sys::MessageNone{};
        });
        connect(typeid(::message::bluetooth::ResponseBondedDevices), [&](sys::Message *msg) {
            auto responseBondedDevicesMsg = static_cast<::message::bluetooth::ResponseBondedDevices *>(msg);
            auto message = static_cast<::message::bluetooth::ResponseBondedDevices *>(msg);
            bluetoothSettingsModel->replaceDevicesList(message->getDevices());

            if (gui::window::name::all_devices == getCurrentWindow()->getName()) {
                auto bondedDevicesData = std::make_unique<gui::BondedDevicesData>(
                    responseBondedDevicesMsg->getDevices(), responseBondedDevicesMsg->getAddressOfConnectedDevice());
                switchWindow(gui::window::name::all_devices, std::move(bondedDevicesData));
                switchWindow(gui::window::name::all_devices);
            }
            return sys::MessageNone{};
        });


@@ 204,13 216,12 @@ namespace app

        connect(typeid(BluetoothPairResultMessage), [&](sys::Message *msg) {
            auto bluetoothPairResultMsg = static_cast<BluetoothPairResultMessage *>(msg);
            auto device                 = bluetoothPairResultMsg->getDevice();
            if (bluetoothPairResultMsg->isSucceed()) {
                bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(),
                                service::name::bluetooth);
                return sys::MessageNone{};
            }
            auto addr    = bluetoothPairResultMsg->getAddr();
            auto message = std::make_shared<BluetoothPairMessage>(std::move(addr));
            auto message = std::make_shared<BluetoothPairMessage>(device);

            switchToAllDevicesViaBtErrorPrompt(std::move(message), "app_settings_bluetooth_pairing_error_message");

            return sys::MessageNone{};


@@ 221,31 232,37 @@ namespace app
            return sys::MessageNone{};
        });

        connect(typeid(::message::bluetooth::ProfileStatus), [&](sys::Message *msg) { return sys::MessageNone{}; });

        connect(typeid(::message::bluetooth::UnpairResult), [&](sys::Message *msg) {
            auto unpairResultMsg = static_cast<::message::bluetooth::UnpairResult *>(msg);
            if (unpairResultMsg->isSucceed()) {
                return sys::MessageNone{};
            }
            auto addr = unpairResultMsg->getAddr();
            bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(), service::name::bluetooth);
            auto message = std::make_shared<message::bluetooth::Unpair>(std::move(addr));
            auto device  = unpairResultMsg->getDevice();
            auto message = std::make_shared<message::bluetooth::Unpair>(device);
            switchToAllDevicesViaBtErrorPrompt(std::move(message), "app_settings_bluetooth_unpairing_error_message");
            return sys::MessageNone{};
        });

        connect(typeid(::message::bluetooth::ConnectResult), [&](sys::Message *msg) {
            auto connectResultMsg = static_cast<::message::bluetooth::ConnectResult *>(msg);
            auto device           = connectResultMsg->getDevice();

            if (connectResultMsg->isSucceed()) {
                bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(),
                                service::name::bluetooth);
                return sys::MessageNone{};
            }
            auto addr    = connectResultMsg->getAddr();
            auto message = std::make_shared<message::bluetooth::Connect>(std::move(addr));

            auto message = std::make_shared<message::bluetooth::Connect>(device);
            switchToAllDevicesViaBtErrorPrompt(std::move(message), "app_settings_bluetooth_connecting_error_message");
            return sys::MessageNone{};
        });

        connect(typeid(::message::bluetooth::DisconnectResult), [&](sys::Message *msg) {
            if (gui::window::name::all_devices == getCurrentWindow()->getName()) {
                switchWindow(gui::window::name::all_devices);
            }
            return sys::MessageNone{};
        });
        connect(typeid(::message::bluetooth::InitializationResult), [&](sys::Message *msg) {
            auto initializationResultMsg = static_cast<::message::bluetooth::InitializationResult *>(msg);
            if (initializationResultMsg->isSucceed()) {


@@ 339,6 356,7 @@ namespace app
            [this](const std::string &value) { notificationsWhenLocked = utils::getNumericValue<bool>(value); },
            ::settings::SettingsScope::Global);

        bluetoothSettingsModel->requestBondedDevices();
        return ret;
    }



@@ 374,11 392,11 @@ namespace app
        windowsFactory.attach(gui::window::name::bluetooth, [](Application *app, const std::string &name) {
            return std::make_unique<gui::BluetoothWindow>(app);
        });
        windowsFactory.attach(gui::window::name::add_device, [](Application *app, const std::string &name) {
            return std::make_unique<gui::AddDeviceWindow>(app);
        windowsFactory.attach(gui::window::name::add_device, [this](Application *app, const std::string &name) {
            return std::make_unique<gui::AddDeviceWindow>(app, bluetoothSettingsModel);
        });
        windowsFactory.attach(gui::window::name::all_devices, [](Application *app, const std::string &name) {
            return std::make_unique<gui::AllDevicesWindow>(app);
        windowsFactory.attach(gui::window::name::all_devices, [this](Application *app, const std::string &name) {
            return std::make_unique<gui::AllDevicesWindow>(app, bluetoothSettingsModel);
        });
        windowsFactory.attach(gui::window::name::phone_name, [](Application *app, const std::string &name) {
            return std::make_unique<gui::PhoneNameWindow>(app);

M module-apps/application-settings/CMakeLists.txt => module-apps/application-settings/CMakeLists.txt +1 -0
@@ 31,6 31,7 @@ target_sources(application-settings
        models/system/SARInfoRepository.cpp
        models/system/TechnicalInformationModel.cpp
        models/system/TechnicalInformationRepository.cpp
        models/bluetooth/BluetoothSettingsModel.cpp
        presenter/network/SimContactsImportWindowPresenter.cpp
        presenter/system/SARInfoWindowPresenter.cpp
        presenter/system/TechnicalWindowPresenter.cpp

M module-apps/application-settings/include/application-settings/ApplicationSettings.hpp => module-apps/application-settings/include/application-settings/ApplicationSettings.hpp +2 -0
@@ 11,6 11,7 @@
#include <bsp/keypad_backlight/keypad_backlight.hpp>
#include <service-evtmgr/screen-light-control/ScreenLightControl.hpp>
#include <EventStore.hpp>
#include <application-settings/models/bluetooth/BluetoothSettingsModel.hpp>

class AudioStopNotification; // Forward declaration



@@ 188,6 189,7 @@ namespace app
        bool callsFromFavorites       = false;
        int connectionFrequency       = 0;
        bool flightModeOn             = true;
        std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel = nullptr;
    };

    template <> struct ManifestTraits<ApplicationSettings>

M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp +84 -25
@@ 8,80 8,139 @@
#include <service-bluetooth/messages/Connect.hpp>
#include <service-bluetooth/messages/DeviceName.hpp>
#include <service-bluetooth/messages/Disconnect.hpp>
#include <service-bluetooth/messages/Status.hpp>
#include <service-bluetooth/messages/SetStatus.hpp>
#include <service-bluetooth/messages/SetDeviceName.hpp>
#include <service-bluetooth/messages/Passkey.hpp>
#include <service-bluetooth/messages/Unpair.hpp>
#include <service-bluetooth/messages/SyncDevices.hpp>

BluetoothSettingsModel::BluetoothSettingsModel(app::Application *application) : application{application}
BluetoothSettingsModel::BluetoothSettingsModel(sys::Service *service) : service{service}
{}

void BluetoothSettingsModel::requestStatus()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestStatus>(), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestStatus>(), service::name::bluetooth);
}

void BluetoothSettingsModel::setStatus(const bool desiredBluetoothState, const bool desiredVisibility)
{
    BluetoothStatus status{.state = desiredBluetoothState ? BluetoothStatus::State::On : BluetoothStatus::State::Off,
                           .visibility = desiredVisibility};
    status.state      = desiredBluetoothState ? BluetoothStatus::State::On : BluetoothStatus::State::Off;
    status.visibility = desiredVisibility;
    message::bluetooth::SetStatus setStatus(status);
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::SetStatus>(std::move(setStatus)),
                                 service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<::message::bluetooth::SetStatus>(std::move(setStatus)),
                             service::name::bluetooth);
}

void BluetoothSettingsModel::requestDeviceName()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestDeviceName>(), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestDeviceName>(), service::name::bluetooth);
}

void BluetoothSettingsModel::setDeviceName(const UTF8 &deviceName)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::SetDeviceName>(deviceName),
                                 service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<message::bluetooth::SetDeviceName>(deviceName), service::name::bluetooth);
}

void BluetoothSettingsModel::requestBondedDevices()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(),
                                 service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(), service::name::bluetooth);
}

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

void BluetoothSettingsModel::stopScan()
{
    application->bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::StopScan),
                                 service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::StopScan),
                             service::name::bluetooth);
}

void BluetoothSettingsModel::requestDevicePair(const std::string &addr)
void BluetoothSettingsModel::requestDevicePair(const Devicei &device)
{
    application->bus.sendUnicast(std::make_shared<BluetoothPairMessage>(addr), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<BluetoothPairMessage>(device), service::name::bluetooth);
}

void BluetoothSettingsModel::requestDeviceUnpair(const std::string &addr)
void BluetoothSettingsModel::requestDeviceUnpair(const Devicei &device)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Unpair>(addr), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<message::bluetooth::Unpair>(device), service::name::bluetooth);
}

void BluetoothSettingsModel::responsePasskey(const std::string &passkey)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::ResponsePasskey>(passkey),
                                 service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<message::bluetooth::ResponsePasskey>(passkey), service::name::bluetooth);
}

void BluetoothSettingsModel::requestConnection(const std::string &addr)
void BluetoothSettingsModel::requestConnection(const Devicei &device)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Connect>(addr), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<message::bluetooth::Connect>(device), service::name::bluetooth);
}

void BluetoothSettingsModel::requestDisconnection()
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Disconnect>(), service::name::bluetooth);
    service->bus.sendUnicast(std::make_shared<message::bluetooth::Disconnect>(), service::name::bluetooth);
}
void BluetoothSettingsModel::replaceDevicesList(const std::vector<Devicei> &devicesList)
{
    devices = devicesList;
}
void BluetoothSettingsModel::setActiveDeviceState(const DeviceState &state)
{
    auto activeDevice = getActiveDevice();
    if (activeDevice.has_value()) {
        activeDevice.value().get().deviceState = state;
    }
}
auto BluetoothSettingsModel::getActiveDevice() -> std::optional<std::reference_wrapper<Devicei>>
{
    try {
        return devices.at(activeDeviceIndex);
    }
    catch (const std::out_of_range &oor) {
        LOG_WARN("NO DEVICE FOUND!");
        return std::nullopt;
    }
}
auto BluetoothSettingsModel::getSelectedDevice() -> std::optional<std::reference_wrapper<Devicei>>
{
    try {
        return devices.at(selectedDeviceIndex);
    }
    catch (const std::out_of_range &oor) {
        LOG_WARN("NO DEVICE FOUND!");
        return std::nullopt;
    }
}
void BluetoothSettingsModel::setActiveDevice(const Devicei &device)
{
    auto itr          = std::find(std::begin(devices), std::end(devices), device);
    activeDeviceIndex = std::distance(std::begin(devices), itr);
}
void BluetoothSettingsModel::setSelectedDevice(const Devicei &device)
{
    auto itr            = std::find(std::begin(devices), std::end(devices), device);
    selectedDeviceIndex = std::distance(std::begin(devices), itr);
}

auto BluetoothSettingsModel::getDevices() -> std::vector<Devicei> &
{
    return devices;
}
auto BluetoothSettingsModel::isDeviceConnecting() -> bool
{

    auto deviceIt = std::find_if(std::begin(devices), std::end(devices), [](const Devicei &device) {
        return device.deviceState == DeviceState::Connecting;
    });

    if (deviceIt != std::end(devices)) {
        return true;
    }
    return false;
}
auto BluetoothSettingsModel::getStatus() const -> const BluetoothStatus
{
    return status;
}

M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.hpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.hpp +21 -5
@@ 4,6 4,8 @@
#pragma once

#include "Application.hpp"
#include <Device.hpp>
#include <service-bluetooth/messages/Status.hpp>

extern "C"
{


@@ 13,7 15,7 @@ extern "C"
class BluetoothSettingsModel
{
  public:
    explicit BluetoothSettingsModel(app::Application *application);
    explicit BluetoothSettingsModel(sys::Service *service);

    void requestStatus();
    void setStatus(bool desiredBluetoothState, bool desiredVisibility);


@@ 22,12 24,26 @@ class BluetoothSettingsModel
    void requestBondedDevices();
    void requestScan();
    void stopScan();
    void requestDevicePair(const std::string &addr);
    void requestDeviceUnpair(const std::string &addr);
    void requestDevicePair(const Devicei &device);
    void requestDeviceUnpair(const Devicei &device);
    void responsePasskey(const std::string &passkey);
    void requestConnection(const std::string &addr);
    void requestConnection(const Devicei &device);
    void requestDisconnection();

    void replaceDevicesList(const std::vector<Devicei> &devicesList);
    void setActiveDeviceState(const DeviceState &state);
    auto getActiveDevice() -> std::optional<std::reference_wrapper<Devicei>>;
    auto getSelectedDevice() -> std::optional<std::reference_wrapper<Devicei>>;
    void setActiveDevice(const Devicei &device);
    void setSelectedDevice(const Devicei &device);
    auto getDevices() -> std::vector<Devicei> &;
    auto isDeviceConnecting() -> bool;
    auto getStatus() const -> const BluetoothStatus;

  private:
    app::Application *application = nullptr;
    std::vector<Devicei> devices{};
    std::uint16_t activeDeviceIndex   = 0;
    std::uint16_t selectedDeviceIndex = 0;
    sys::Service *service             = nullptr;
    BluetoothStatus status{};
};

M module-apps/application-settings/windows/bluetooth/AddDeviceWindow.cpp => module-apps/application-settings/windows/bluetooth/AddDeviceWindow.cpp +6 -7
@@ 12,9 12,10 @@
namespace gui
{

    AddDeviceWindow::AddDeviceWindow(app::Application *app) : BaseSettingsWindow(app, window::name::add_device)
    AddDeviceWindow::AddDeviceWindow(app::Application *app,
                                     std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel)
        : BaseSettingsWindow(app, window::name::add_device), bluetoothSettingsModel(bluetoothSettingsModel)
    {
        bluetoothSettingsModel = std::make_unique<BluetoothSettingsModel>(application);
    }

    void AddDeviceWindow::onBeforeShow(ShowMode /*mode*/, SwitchData *data)


@@ 35,13 36,11 @@ namespace gui
    {
        std::list<gui::Option> optionsList;

        for (const auto &device : devices) {
        for (auto &device : devices) {
            optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
                device.name,
                [=](gui::Item & /*unused*/) {
                    auto pairingDeviceData = std::make_unique<PairingDeviceData>(device);
                    application->switchWindow(gui::window::name::all_devices, std::move(pairingDeviceData));
                    bluetoothSettingsModel->requestDevicePair(bd_addr_to_str(device.address));
                [&](gui::Item & /*unused*/) {
                    bluetoothSettingsModel->requestDevicePair(device);
                    application->switchWindow(gui::window::name::all_devices);
                    return true;
                },

M module-apps/application-settings/windows/bluetooth/AddDeviceWindow.hpp => module-apps/application-settings/windows/bluetooth/AddDeviceWindow.hpp +2 -2
@@ 13,14 13,14 @@ namespace gui
    class AddDeviceWindow : public BaseSettingsWindow
    {
      public:
        explicit AddDeviceWindow(app::Application *app);
        AddDeviceWindow(app::Application *app, std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel);

      private:
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        void onClose(CloseReason reason) override;
        auto buildOptionsList() -> std::list<Option> override;

        std::unique_ptr<BluetoothSettingsModel> bluetoothSettingsModel;
        std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel{};
        std::vector<Devicei> devices;
    };
}; // namespace gui

M module-apps/application-settings/windows/bluetooth/AllDevicesWindow.cpp => module-apps/application-settings/windows/bluetooth/AllDevicesWindow.cpp +60 -74
@@ 4,8 4,8 @@
#include "AllDevicesWindow.hpp"

#include <application-settings/data/BondedDevicesData.hpp>
#include <application-settings/data/PairingDeviceData.hpp>
#include <application-settings/windows/WindowNames.hpp>
#include <application-settings/widgets/SettingsStyle.hpp>

#include <DialogMetadataMessage.hpp>
#include <InputEvent.hpp>


@@ 15,10 15,10 @@
namespace gui
{

    AllDevicesWindow::AllDevicesWindow(app::Application *app) : BaseSettingsWindow(app, window::name::all_devices)
    AllDevicesWindow::AllDevicesWindow(app::Application *app,
                                       std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel)
        : BaseSettingsWindow(app, window::name::all_devices), bluetoothSettingsModel(bluetoothSettingsModel)
    {
        bluetoothSettingsModel = std::make_unique<BluetoothSettingsModel>(application);
        bluetoothSettingsModel->requestBondedDevices();
        buildInterface();
    }



@@ 45,31 45,9 @@ namespace gui
    {
        if (mode == ShowMode::GUI_SHOW_RETURN) {
            bluetoothSettingsModel->stopScan();
            if (activeDevice.state == ActiveDevice::DeviceState::Connecting) {
                activeDevice.address.clear();
                activeDevice.state = ActiveDevice::DeviceState::Unknown;
            }
            else if (activeDevice.state == ActiveDevice::DeviceState::Pairing) {
                devices.erase(std::remove_if(devices.begin(),
                                             devices.end(),
                                             [&](const auto &device) {
                                                 return (bd_addr_to_str(device.address) == activeDevice.address);
                                             }),
                              devices.end());

                activeDevice.address.clear();
                activeDevice.state = ActiveDevice::DeviceState::Unknown;
            }
        }
        if (const auto bondedDevicesData = dynamic_cast<BondedDevicesData *>(data); bondedDevicesData != nullptr) {
            devices              = bondedDevicesData->getDevices();
            activeDevice.address = bondedDevicesData->getAddressOfConnectedDevice();
            activeDevice.state   = ActiveDevice::DeviceState::Connected;
        }
        else if (const auto pairingDeviceData = dynamic_cast<PairingDeviceData *>(data); pairingDeviceData != nullptr) {
            activeDevice.address = bd_addr_to_str(pairingDeviceData->getPairingDevice().address);
            activeDevice.state   = ActiveDevice::DeviceState::Pairing;
            devices.emplace_back(pairingDeviceData->getPairingDevice());
            bluetoothSettingsModel->replaceDevicesList(bondedDevicesData->getDevices());
        }
        refreshOptionsList();
    }


@@ 90,15 68,12 @@ namespace gui
            return true;
        }
        if (inputEvent.is(KeyCode::KEY_LF)) {
            devices.erase(std::remove_if(devices.begin(),
                                         devices.end(),
                                         [&](const auto &device) {
                                             return (bd_addr_to_str(device.address) == addressOfDeviceSelected);
                                         }),
                          devices.end());
            bottomBar->setActive(BottomBar::Side::LEFT, false);
            bottomBar->setActive(BottomBar::Side::CENTER, false);
            bluetoothSettingsModel->requestDeviceUnpair(addressOfDeviceSelected);
            auto selectedDevice = bluetoothSettingsModel->getSelectedDevice();
            if (selectedDevice.has_value()) {
                bluetoothSettingsModel->requestDeviceUnpair(selectedDevice.value().get());
            }
            refreshOptionsList();
            return true;
        }


@@ 107,30 82,25 @@ namespace gui

    auto AllDevicesWindow::buildOptionsList() -> std::list<Option>
    {
        bottomBar->setActive(BottomBar::Side::CENTER, !devices.empty());

        bottomBar->setActive(BottomBar::Side::CENTER, !bluetoothSettingsModel->getDevices().empty());
        std::list<gui::Option> optionsList;
        for (const auto &device : devices) {
            ActiveDevice newDevice(bd_addr_to_str(device.address));
            newDevice.address == activeDevice.address ? newDevice.state = activeDevice.state
                                                      : newDevice.state = ActiveDevice::DeviceState::Paired;
            UTF8 textOnCenter                                           = getTextOnCenter(newDevice.state);
            option::SettingRightItem rightItem                          = getRightItem(newDevice.state);
            UTF8 textOnRight                                            = getTextOnRight(newDevice.state);

        for (const auto &device : bluetoothSettingsModel->getDevices()) {
            UTF8 textOnCenter                  = getTextOnCenter(device.deviceState);
            option::SettingRightItem rightItem = getRightItem(device.deviceState);
            UTF8 textOnRight                   = getTextOnRight(device.deviceState);

            optionsList.emplace_back(std::make_unique<gui::option::OptionSettings>(
                device.name,
                [=](gui::Item & /*item*/) {
                    return handleDeviceAction(newDevice);
                },
                [=](gui::Item & /*item*/) { return handleDeviceAction(device); },
                [=](gui::Item &item) {
                    if (item.focus) {
                        this->setBottomBarText(textOnCenter, BottomBar::Side::CENTER);
                        if (newDevice.state != ActiveDevice::DeviceState::Pairing) {
                        if (device.deviceState != DeviceState::Pairing) {
                            this->setBottomBarText(utils::translate("common_forget"), BottomBar::Side::LEFT);
                            this->bottomBar->setActive(BottomBar::Side::LEFT, true);
                        }
                        addressOfDeviceSelected = newDevice.address;
                        bluetoothSettingsModel->setSelectedDevice(device);
                    }
                    return true;
                },


@@ 142,64 112,80 @@ namespace gui
        return optionsList;
    }

    UTF8 AllDevicesWindow::getTextOnCenter(const ActiveDevice::DeviceState &state) const
    UTF8 AllDevicesWindow::getTextOnCenter(const DeviceState &state) const
    {
        switch (state) {
        case ActiveDevice::DeviceState::Connected:
        case DeviceState::Connected:
            return utils::translate("common_disconnect");
        case ActiveDevice::DeviceState::Paired:
        case DeviceState::Paired:
            if (bluetoothSettingsModel->isDeviceConnecting()) {
                break;
            }
            return utils::translate("common_connect");
        case ActiveDevice::DeviceState::Pairing:
        case ActiveDevice::DeviceState::Connecting:
        case ActiveDevice::DeviceState::Unknown:
        case DeviceState::Pairing:
            [[fallthrough]];
        case DeviceState::Connecting:
            [[fallthrough]];
        case DeviceState::Unknown:
            [[fallthrough]];
        case DeviceState::ConnectedAudio:
            [[fallthrough]];
        case DeviceState::ConnectedVoice:
            break;
        }
        return UTF8();
    }

    UTF8 AllDevicesWindow::getTextOnRight(const ActiveDevice::DeviceState &state) const
    UTF8 AllDevicesWindow::getTextOnRight(const DeviceState &state) const
    {
        switch (state) {
        case ActiveDevice::DeviceState::Connected:
        case DeviceState::Connected:
            return utils::translate("app_settings_option_connected");
        case ActiveDevice::DeviceState::Connecting:
        case DeviceState::Connecting:
            return utils::translate("app_settings_option_connecting");
        case ActiveDevice::DeviceState::Pairing:
        case DeviceState::Pairing:
            return utils::translate("app_settings_option_pairing");
        case ActiveDevice::DeviceState::Paired:
        case ActiveDevice::DeviceState::Unknown:
        case DeviceState::Paired:
            [[fallthrough]];
        case DeviceState::Unknown:
            [[fallthrough]];
        case DeviceState::ConnectedAudio:
            [[fallthrough]];
        case DeviceState::ConnectedVoice:
            break;
        }
        return UTF8();
    }

    option::SettingRightItem AllDevicesWindow::getRightItem(const ActiveDevice::DeviceState &state) const
    option::SettingRightItem AllDevicesWindow::getRightItem(const DeviceState &state) const
    {
        switch (state) {
        case ActiveDevice::DeviceState::Connected:
        case ActiveDevice::DeviceState::Connecting:
        case ActiveDevice::DeviceState::Pairing:
        case DeviceState::Connected:
            [[fallthrough]];
        case DeviceState::Connecting:
            [[fallthrough]];
        case DeviceState::Pairing:
            return option::SettingRightItem::Text;
        case ActiveDevice::DeviceState::Paired:
        case DeviceState::Paired:
            return option::SettingRightItem::Bt;
        case ActiveDevice::DeviceState::Unknown:
        case DeviceState::Unknown:
            [[fallthrough]];
        case DeviceState::ConnectedAudio:
            [[fallthrough]];
        case DeviceState::ConnectedVoice:
            break;
        }
        return option::SettingRightItem::Disabled;
    }

    auto AllDevicesWindow::handleDeviceAction(const ActiveDevice &newDevice) -> bool
    auto AllDevicesWindow::handleDeviceAction(const Devicei &device) -> bool
    {
        if (newDevice.state == ActiveDevice::DeviceState::Connected) {
            activeDevice.address.clear();
            activeDevice.state = ActiveDevice::DeviceState::Unknown;
        if (device.deviceState == DeviceState::Connected) {
            bluetoothSettingsModel->requestDisconnection();
            refreshOptionsList();
        }
        else if (newDevice.state == ActiveDevice::DeviceState::Paired) {
            activeDevice.address = newDevice.address;
            activeDevice.state   = ActiveDevice::DeviceState::Connecting;
            bluetoothSettingsModel->requestConnection(newDevice.address);
        else if (device.deviceState == DeviceState::Paired && !bluetoothSettingsModel->isDeviceConnecting()) {
            bluetoothSettingsModel->requestConnection(device);
            refreshOptionsList();
        }
        return true;

M module-apps/application-settings/windows/bluetooth/AllDevicesWindow.hpp => module-apps/application-settings/windows/bluetooth/AllDevicesWindow.hpp +7 -30
@@ 2,52 2,29 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once

#include <application-settings/models/bluetooth/BluetoothSettingsModel.hpp>
#include <application-settings/windows/BaseSettingsWindow.hpp>

#include <Device.hpp>
#include <application-settings/models/bluetooth/BluetoothSettingsModel.hpp>

namespace gui
{
    class Image;

    class ActiveDevice
    {
      public:
        explicit ActiveDevice(std::string address) : address(std::move(address))
        {}
        ActiveDevice() = default;
        enum class DeviceState
        {
            Connected,
            Connecting,
            Pairing,
            Paired,
            Unknown
        };
        DeviceState state = DeviceState::Unknown;
        std::string address;
    };

    class AllDevicesWindow : public BaseSettingsWindow
    {
      public:
        explicit AllDevicesWindow(app::Application *app);
        AllDevicesWindow(app::Application *app, std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel);

      private:
        void buildInterface() override;
        auto buildOptionsList() -> std::list<Option> override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto onInput(const InputEvent &inputEvent) -> bool override;
        UTF8 getTextOnCenter(const ActiveDevice::DeviceState &) const;
        UTF8 getTextOnRight(const ActiveDevice::DeviceState &) const;
        option::SettingRightItem getRightItem(const ActiveDevice::DeviceState &) const;
        auto handleDeviceAction(const ActiveDevice &) -> bool;
        UTF8 getTextOnCenter(const DeviceState &) const;
        UTF8 getTextOnRight(const DeviceState &) const;
        option::SettingRightItem getRightItem(const DeviceState &) const;
        auto handleDeviceAction(const Devicei &) -> bool;

        ActiveDevice activeDevice;
        std::vector<Devicei> devices{};
        std::string addressOfDeviceSelected;
        std::unique_ptr<BluetoothSettingsModel> bluetoothSettingsModel{};
        std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel{};
    };

} // namespace gui

M module-apps/application-settings/windows/bluetooth/PhoneNameWindow.hpp => module-apps/application-settings/windows/bluetooth/PhoneNameWindow.hpp +0 -1
@@ 3,7 3,6 @@
#pragma once

#include <application-settings/models/bluetooth/BluetoothSettingsModel.hpp>

#include <Text.hpp>

namespace gui

M module-apps/tests/CMakeLists.txt => module-apps/tests/CMakeLists.txt +1 -0
@@ 5,6 5,7 @@ add_catch2_executable(
        tests-main.cpp
        test-CallbackStorage.cpp
        test-PhoneModesPolicies.cpp
        tests-BluetoothSettingsModel.cpp
    LIBS
        module-apps
)

A module-apps/tests/tests-BluetoothSettingsModel.cpp => module-apps/tests/tests-BluetoothSettingsModel.cpp +145 -0
@@ 0,0 1,145 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>
#include "Device.hpp"
#include "application-settings/models/bluetooth/BluetoothSettingsModel.hpp"

TEST_CASE("Devicei comparison")
{
    Devicei device1{"Dev1"};
    Devicei device2{"Dev2"};
    Devicei device3{"Dev1"};

    bd_addr_t addr1;
    bd_addr_t addr2;

    std::string addr1Str{"00:12:6F:E7:9D:05"};
    std::string addr2Str{"F8:5C:7D:17:E4:8F"};

    sscanf_bd_addr(addr1Str.c_str(), addr1);
    sscanf_bd_addr(addr2Str.c_str(), addr2);

    SECTION("Same addresses and names")
    {
        device1.setAddress(&addr1);
        device3.setAddress(&addr1);
        REQUIRE(device1 == device3);
    }

    SECTION("Different addresses and names")
    {
        device1.setAddress(&addr1);
        device2.setAddress(&addr2);
        REQUIRE_FALSE(device1 == device3);
    }

    SECTION("Same names and different addresses")
    {
        device1.setAddress(&addr1);
        device3.setAddress(&addr2);
        REQUIRE_FALSE(device1 == device3);
    }
}

TEST_CASE("Device handling")
{
    BluetoothSettingsModel settingsModel{nullptr};

    Devicei device1{"Dev1"};
    Devicei device2{"Dev2"};
    Devicei device3{"Dev1"};
    Devicei device4{"Dev4"};
    Devicei dummy{""};

    bd_addr_t addr1;
    bd_addr_t addr2;
    bd_addr_t addr3;

    std::string addr1Str{"00:12:6F:E7:9D:05"};
    std::string addr2Str{"F8:5C:7D:17:E4:8F"};
    std::string addr3Str{"F8:5C:7D:17:E4:00"};

    sscanf_bd_addr(addr1Str.c_str(), addr1);
    sscanf_bd_addr(addr2Str.c_str(), addr2);
    sscanf_bd_addr(addr3Str.c_str(), addr3);

    device1.setAddress(&addr1);
    device2.setAddress(&addr2);
    device3.setAddress(&addr1);
    device4.setAddress(&addr2);

    std::vector<Devicei> devicesList;

    devicesList.push_back(device1);
    devicesList.push_back(device2);
    devicesList.push_back(device3);

    SECTION("Replace devices list")
    {
        settingsModel.replaceDevicesList(devicesList);
        REQUIRE(settingsModel.getDevices().size() == 3);
        REQUIRE(settingsModel.getDevices() == devicesList);
    }

    SECTION("Set active device")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setActiveDevice(device2);
        REQUIRE(settingsModel.getActiveDevice().has_value());
        REQUIRE(settingsModel.getActiveDevice().value().get() == device2);
    }

    SECTION("Set wrong active device")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setActiveDevice(device4);
        REQUIRE_FALSE(settingsModel.getActiveDevice().has_value());
    }

    SECTION("Set selected device")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setSelectedDevice(device2);
        REQUIRE(settingsModel.getSelectedDevice().has_value());
        REQUIRE(settingsModel.getSelectedDevice().value().get() == device2);
    }

    SECTION("Set wrong selected device")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setSelectedDevice(device4);
        REQUIRE_FALSE(settingsModel.getSelectedDevice().has_value());
    }

    SECTION("Set active device state")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setActiveDevice(device2);
        settingsModel.setActiveDeviceState(DeviceState::Connected);
        REQUIRE(settingsModel.getActiveDevice().value().get().deviceState == DeviceState::Connected);
    }

    SECTION("Is device connecting? - true")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setActiveDevice(device2);
        settingsModel.setActiveDeviceState(DeviceState::Connecting);
        REQUIRE(settingsModel.isDeviceConnecting());
    }

    SECTION("Is device connecting? - false")
    {
        settingsModel.replaceDevicesList(devicesList);

        settingsModel.setActiveDevice(device2);
        settingsModel.setActiveDeviceState(DeviceState::Connected);
        REQUIRE_FALSE(settingsModel.isDeviceConnecting());
    }
}

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +3 -1
@@ 158,7 158,9 @@ auto BluetoothWorker::handleCommand(QueueHandle_t queue) -> bool
        break;
    case bluetooth::Command::Unpair:
        controller->processCommand(command);
        removeFromBoundDevices(command.getAddress());
        removeFromBoundDevices(command.getDevice().address);
        break;
    case bluetooth::Command::None:
        break;
    default:
        controller->processCommand(command);

M module-bluetooth/Bluetooth/CommandHandler.cpp => module-bluetooth/Bluetooth/CommandHandler.cpp +10 -10
@@ 52,15 52,15 @@ namespace bluetooth
        case bluetooth::Command::StartPan:
            return startPan();
        case bluetooth::Command::Pair:
            return pair(command.getAddress());
            return pair(command.getDevice());
        case bluetooth::Command::Unpair:
            return unpair(command.getAddress());
            return unpair(command.getDevice());
        case bluetooth::Command::VisibilityOn:
            return setVisibility(true);
        case bluetooth::Command::VisibilityOff:
            return setVisibility(false);
        case bluetooth::Command::ConnectAudio:
            return establishAudioConnection(command.getAddress());
            return establishAudioConnection(command.getDevice());
        case bluetooth::Command::DisconnectAudio:
            return disconnectAudioConnection();
        case bluetooth::Command::PowerOff:


@@ 122,11 122,11 @@ namespace bluetooth
        return Error::Success;
    }

    Error::Code CommandHandler::establishAudioConnection(uint8_t *addr)
    Error::Code CommandHandler::establishAudioConnection(const Devicei &device)
    {
        profileManager->init();
        LOG_INFO("Connecting audio");
        profileManager->connect(addr);
        profileManager->connect(device);
        return Error::Success;
    }



@@ 136,10 136,10 @@ namespace bluetooth
        profileManager->disconnect();
        return Error::Success;
    }
    Error::Code CommandHandler::pair(bd_addr_t addr)
    Error::Code CommandHandler::pair(const Devicei &device)
    {
        LOG_INFO("Pairing...");
        const auto error_code = driver->pair(addr) ? Error::Success : Error::LibraryError;
        const auto error_code = driver->pair(device) ? Error::Success : Error::LibraryError;
        LOG_INFO("Pairing result: %s", magic_enum::enum_name(error_code).data());
        return error_code;
    }


@@ 157,13 157,13 @@ namespace bluetooth
        profileManager->switchProfile(profile);
        return Error::Success;
    }
    Error::Code CommandHandler::unpair(uint8_t *addr)
    Error::Code CommandHandler::unpair(const Devicei &device)
    {
        LOG_INFO("Unpairing...");
        if (profileManager->isAddressActuallyUsed(addr)) {
        if (profileManager->isAddressActuallyUsed(device.address)) {
            profileManager->disconnect();
        }
        const auto error_code = driver->unpair(addr) ? Error::Success : Error::LibraryError;
        const auto error_code = driver->unpair(device) ? Error::Success : Error::LibraryError;
        LOG_INFO("Unpairing result: %s", magic_enum::enum_name(error_code).data());
        return error_code;
    }

M module-bluetooth/Bluetooth/CommandHandler.hpp => module-bluetooth/Bluetooth/CommandHandler.hpp +9 -12
@@ 44,13 44,10 @@ namespace bluetooth
            None,
        };

        explicit Command(Command::Type type, std::optional<uint8_t *> addr = std::nullopt) : type(type)
        explicit Command(Command::Type type, std::optional<Devicei> dev = std::nullopt) : type(type)
        {
            if (addr) {
                bd_addr_copy(address, addr.value());
            }
            else {
                memset(address, 0, sizeof(bd_addr_t));
            if (dev.has_value()) {
                device = dev.value();
            }
        }



@@ 59,13 56,13 @@ namespace bluetooth
            return type;
        }

        auto getAddress() -> uint8_t *
        auto getDevice() const noexcept -> Devicei
        {
            return address;
            return device;
        }

      private:
        bd_addr_t address{};
        Devicei device{};
        Type type;
    };



@@ 92,10 89,10 @@ namespace bluetooth
        Error::Code stopScan();
        Error::Code startPan();
        Error::Code setVisibility(bool visibility);
        Error::Code establishAudioConnection(bd_addr_t addr);
        Error::Code establishAudioConnection(const Devicei &device);
        Error::Code disconnectAudioConnection();
        Error::Code pair(bd_addr_t addr);
        Error::Code unpair(bd_addr_t addr);
        Error::Code pair(const Devicei &device);
        Error::Code unpair(const Devicei &device);
        Error::Code availableDevices();
        Error::Code switchAudioProfile();
        sys::Service *service;

M module-bluetooth/Bluetooth/Device.hpp => module-bluetooth/Bluetooth/Device.hpp +32 -2
@@ 5,6 5,7 @@
#include <array>
#include <cstring>
#include <module-bluetooth/lib/btstack/src/bluetooth.h>
#include <btstack_util.h>
#include <string>
#include <utility>



@@ 73,6 74,17 @@ static inline std::string getListOfSupportedServicesInString(uint32_t cod)
    return res;
}

enum class DeviceState
{
    Connected,
    ConnectedAudio,
    ConnectedVoice,
    Connecting,
    Pairing,
    Paired,
    Unknown
};

struct Devicei : public Device
{
  public:


@@ 81,14 93,32 @@ struct Devicei : public Device
    uint16_t clockOffset;
    uint32_t classOfDevice; ///> Class of Device/Service
    DEVICE_STATE state;
    DeviceState deviceState;

    Devicei(std::string name = "") : Device(std::move(name))
    {}
    explicit Devicei(std::string name = "") : Device(std::move(name))
    {
        memset(address, 0, sizeof(address));
    }
    ~Devicei() override = default;
    void setAddress(bd_addr_t *addr)
    {
        memcpy(&address, addr, sizeof address);
    }

    inline bool operator==(const Devicei &cmpDevice) const
    {
        return (cmpDevice.name == name) && (bd_addr_cmp(cmpDevice.address, address) == 0);
    }

    inline bool operator!=(const Devicei &cmpDevice) const
    {
        return (cmpDevice.name != name) || (bd_addr_cmp(cmpDevice.address, address) != 0);
    }

    auto address_str() const -> const char *
    {
        return bd_addr_to_str(address);
    }
};

struct DeviceMetadata_t

M module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp => module-bluetooth/Bluetooth/interface/BluetoothDriver.hpp +3 -2
@@ 4,6 4,7 @@
#pragma once

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

namespace bluetooth


@@ 20,8 21,8 @@ namespace bluetooth
        [[nodiscard]] virtual auto scan() -> Error                                               = 0;
        virtual void stopScan()                                                                  = 0;
        virtual void setVisibility(bool visibility)                                              = 0;
        [[nodiscard]] virtual auto pair(uint8_t *addr, std::uint8_t protectionLevel = 0) -> bool = 0;
        [[nodiscard]] virtual auto unpair(uint8_t *addr) -> bool                                 = 0;
        [[nodiscard]] virtual auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool = 0;
        [[nodiscard]] virtual auto unpair(Devicei device) -> bool                                 = 0;

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

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp +5 -4
@@ 203,12 203,13 @@ namespace bluetooth
    {
        gap->setVisibility(visibility);
    }
    auto Driver::pair(uint8_t *addr, std::uint8_t protectionLevel) -> bool
    auto Driver::pair(Devicei device, std::uint8_t protectionLevel) -> bool
    {
        return gap->pair(addr, protectionLevel);
        LOG_FATAL("Device: %s, addr: %s", device.name.c_str(), device.address_str());
        return gap->pair(device, protectionLevel);
    }
    auto Driver::unpair(uint8_t *addr) -> bool
    auto Driver::unpair(Devicei device) -> bool
    {
        return gap->unpair(addr);
        return gap->unpair(device);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.hpp +2 -2
@@ 40,7 40,7 @@ namespace bluetooth
        auto scan() -> Error override;
        void stopScan() override;
        void setVisibility(bool visibility) override;
        auto pair(uint8_t *addr, std::uint8_t protectionLevel = 0) -> bool override;
        auto unpair(uint8_t *addr) -> bool override;
        auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool override;
        auto unpair(Devicei device) -> bool override;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +15 -15
@@ 64,9 64,9 @@ namespace bluetooth
        return pimpl->init();
    }

    void A2DP::setDeviceAddress(uint8_t *addr)
    void A2DP::setDevice(const Devicei &device)
    {
        pimpl->setDeviceAddress(addr);
        pimpl->setDevice(device);
    }

    void A2DP::setOwnerService(const sys::Service *service)


@@ 118,7 118,6 @@ namespace bluetooth
    const sys::Service *A2DP::A2DPImpl::ownerService;
    QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr;
    QueueHandle_t A2DP::A2DPImpl::sinkQueue   = nullptr;
    bd_addr_t A2DP::A2DPImpl::deviceAddr;
    DeviceMetadata_t A2DP::A2DPImpl::metadata;
    btstack_packet_callback_registration_t A2DP::A2DPImpl::hciEventCallbackRegistration;
    std::array<uint8_t, 150> A2DP::A2DPImpl::sdpSourceServiceBuffer;


@@ 130,6 129,7 @@ namespace bluetooth
    std::shared_ptr<BluetoothAudioDevice> A2DP::A2DPImpl::audioDevice;

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

    auto A2DP::A2DPImpl::init() -> Error::Code


@@ 329,9 329,8 @@ namespace bluetooth
                AVRCP::mediaTracker.a2dp_cid = 0;

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


@@ 342,7 341,7 @@ namespace bluetooth
                     AVRCP::mediaTracker.local_seid);
            isConnected    = true;
            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(std::move(deviceAddress), true),
            busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, true),
                                 service::name::bluetooth);
            break;
        }


@@ 550,7 549,9 @@ namespace bluetooth

                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_STOPPED);
            }

            auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
            busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
                                 service::name::bluetooth);
            stopTimer(&AVRCP::mediaTracker);
            break;
        }


@@ 560,9 561,6 @@ namespace bluetooth
                AVRCP::mediaTracker.avrcp_cid = 0;
                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<message::bluetooth::DisconnectResult>(),
                                     service::name::bluetooth);
            }
            isConnected = false;
            break;


@@ 577,19 575,21 @@ namespace bluetooth
            disconnect();
        }
        LOG_INFO("Starting playback");
        a2dp_source_establish_stream(deviceAddr, &AVRCP::mediaTracker.a2dp_cid);
        a2dp_source_establish_stream(device.address, &AVRCP::mediaTracker.a2dp_cid);
    }

    void A2DP::A2DPImpl::disconnect()
    {
        LOG_INFO("Stopping playback");
        a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
        auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
        busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device), service::name::bluetooth);
    }

    void A2DP::A2DPImpl::setDeviceAddress(bd_addr_t addr)
    void A2DP::A2DPImpl::setDevice(const Devicei &dev)
    {
        bd_addr_copy(deviceAddr, addr);
        LOG_INFO("Address set!");
        device = dev;
        LOG_INFO("Device set!");
    }

    void A2DP::A2DPImpl::setOwnerService(const sys::Service *service)

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +1 -1
@@ 23,7 23,7 @@ namespace bluetooth
        auto operator=(A2DP &&other) noexcept -> A2DP &;

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

        void connect() override;

M module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +2 -2
@@ 34,7 34,6 @@ namespace bluetooth
        static constexpr int MEDIA_CAP_SIZE         = 4;

        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpSourceServiceBuffer;
        static bd_addr_t deviceAddr;
        static btstack_packet_callback_registration_t hciEventCallbackRegistration;
        static std::array<uint8_t, MEDIA_CAP_SIZE> mediaSbcCodecCapabilities;
        static const sys::Service *ownerService;


@@ 51,6 50,7 @@ namespace bluetooth
        static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
        static bool isConnected;
        static std::shared_ptr<BluetoothAudioDevice> audioDevice;
        static Devicei device;

      public:
        auto init() -> Error::Code;


@@ 58,7 58,7 @@ namespace bluetooth
        void disconnect();
        void start();
        void stop();
        void setDeviceAddress(bd_addr_t addr);
        void setDevice(const Devicei &dev);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp +16 -20
@@ 8,7 8,6 @@
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-bluetooth/messages/Unpair.hpp>
#include <service-bluetooth/messages/Passkey.hpp>
#include <application-settings/ApplicationSettings.hpp>
extern "C"
{
#include "btstack.h"


@@ 16,7 15,7 @@ extern "C"
};
namespace bluetooth
{
    std::string GAP::currentlyProcessedDeviceAddr;
    Devicei GAP::currentlyProccesedDevice;
    sys::Service *GAP::ownerService = nullptr;
    std::vector<Devicei> GAP::devices;
    btstack_packet_callback_registration_t GAP::cb_handler;


@@ 59,16 58,18 @@ namespace bluetooth
        LOG_INFO("Visibility: %s", visibility ? "true" : "false");
    }

    auto GAP::pair(std::uint8_t *addr, std::uint8_t protectionLevel) -> bool
    auto GAP::pair(const Devicei &device, std::uint8_t protectionLevel) -> bool
    {
        if (hci_get_state() == HCI_STATE_WORKING) {
            currentlyProcessedDeviceAddr = bd_addr_to_str(addr);
            return gap_dedicated_bonding(addr, protectionLevel) == 0;
            auto devIndex            = getDeviceIndexForAddress(devices, device.address);
            currentlyProccesedDevice = devices.at(devIndex);

            return gap_dedicated_bonding(const_cast<uint8_t *>(device.address), protectionLevel) == 0;
        }
        return false;
    }

    auto getDeviceIndexForAddress(const std::vector<Devicei> &devs, bd_addr_t addr) -> int
    auto GAP::getDeviceIndexForAddress(const std::vector<Devicei> &devs, const bd_addr_t addr) -> int
    {
        auto result = std::find_if(std::begin(devs), std::end(devs), [addr](const Devicei &device) {
            return bd_addr_cmp(addr, device.address) == 0;


@@ 84,8 85,7 @@ namespace bluetooth
    void GAP::sendDevices()
    {
        auto msg = std::make_shared<message::bluetooth::ResponseVisibleDevices>(devices);
        ownerService->bus.sendUnicast(msg, "ApplicationSettings");
        ownerService->bus.sendUnicast(std::move(msg), app::name_settings);
        ownerService->bus.sendMulticast(std::move(msg), sys::BusChannel::BluetoothNotifications);
    }

    auto GAP::startScan() -> int


@@ 207,9 207,8 @@ namespace bluetooth
    {
        auto result = packet[2];

        auto msg = std::make_shared<BluetoothPairResultMessage>(currentlyProcessedDeviceAddr, result == 0u);
        currentlyProcessedDeviceAddr.clear();
        ownerService->bus.sendUnicast(std::move(msg), app::name_settings);
        auto msg = std::make_shared<BluetoothPairResultMessage>(currentlyProccesedDevice, result == 0u);
        ownerService->bus.sendUnicast(std::move(msg), "ServiceBluetooth");
    }
    /* @text In ACTIVE, the following events are processed:
     *  - GAP Inquiry result event: BTstack provides a unified inquiry result that contain


@@ 262,7 261,7 @@ namespace bluetooth
            LOG_DEBUG("PIN code request!");
            hci_event_pin_code_request_get_bd_addr(packet, address);
            auto msg = std::make_shared<::message::bluetooth::RequestPasskey>();
            ownerService->bus.sendUnicast(std::move(msg), app::name_settings);
            ownerService->bus.sendMulticast(std::move(msg), sys::BusChannel::BluetoothNotifications);
        }
        const auto eventType = hci_event_packet_get_type(packet);
        switch (state) {


@@ 285,21 284,18 @@ namespace bluetooth
    {
        return devices;
    }
    auto GAP::unpair(uint8_t *addr) -> bool
    auto GAP::unpair(const Devicei &device) -> bool
    {
        LOG_INFO("Unpairing device");
        gap_drop_link_key_for_bd_addr(addr);
        gap_drop_link_key_for_bd_addr(const_cast<uint8_t *>(device.address));

        LOG_INFO("Device unpaired");
        std::string unpairedDevAddr{bd_addr_to_str(addr)};
        ownerService->bus.sendUnicast(
            std::make_shared<message::bluetooth::UnpairResult>(std::move(unpairedDevAddr), true), app::name_settings);
        ownerService->bus.sendMulticast(std::make_shared<message::bluetooth::UnpairResult>(device, true),
                                        sys::BusChannel::BluetoothNotifications);
        return true;
    }
    void GAP::respondPinCode(const std::string &pin)
    {
        bd_addr_t address{};
        sscanf_bd_addr(currentlyProcessedDeviceAddr.c_str(), address);
        gap_pin_code_response(address, pin.c_str());
        gap_pin_code_response(currentlyProccesedDevice.address, pin.c_str());
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp +4 -4
@@ 42,6 42,7 @@ namespace bluetooth
        static void processNameRequestComplete(std::uint8_t *packet, bd_addr_t &addr);
        static void processDedicatedBondingCompleted(std::uint8_t *packet, bd_addr_t &addr);
        static void initStateHandler(std::uint8_t eventType, std::uint8_t *packet);
        static auto getDeviceIndexForAddress(const std::vector<Devicei> &devs, const bd_addr_t addr) -> int;

      public:
        /// THIS have to be called prior to Bt system start!


@@ 49,13 50,12 @@ namespace bluetooth
        auto scan() -> Error;
        void stopScan();
        void setVisibility(bool visibility);
        auto pair(uint8_t *addr, std::uint8_t protectionLevel = 0) -> bool;
        auto unpair(uint8_t *addr) -> bool;
        auto pair(const Devicei &device, std::uint8_t protectionLevel = 0) -> bool;
        auto unpair(const Devicei &device) -> bool;
        static auto getDevicesList() -> const std::vector<Devicei> &;
        static auto isServiceSupportedByRemote(bd_addr_t addr, uint32_t typeOfService) -> bool;
        static void respondPinCode(const std::string &pin);

        static std::string currentlyProcessedDeviceAddr;
        static Devicei currentlyProccesedDevice;
        explicit GAP(sys::Service *owner);
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp +7 -5
@@ 40,9 40,9 @@ namespace bluetooth
        return pimpl->init();
    }

    void HFP::setDeviceAddress(uint8_t *addr)
    void HFP::setDevice(const Devicei &device)
    {
        pimpl->setDeviceAddress(addr);
        pimpl->setDevice(device);
    }

    void HFP::setOwnerService(const sys::Service *service)


@@ 116,6 116,8 @@ namespace bluetooth
        {1, 1},
        {2, 1},
    };
    Devicei HFP::HFPImpl::device;

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


@@ 357,10 359,10 @@ namespace bluetooth
        hfp_ag_release_service_level_connection(aclHandle);
    }

    void HFP::HFPImpl::setDeviceAddress(bd_addr_t addr)
    void HFP::HFPImpl::setDevice(Devicei dev)
    {
        bd_addr_copy(deviceAddr, addr);
        LOG_DEBUG("Address set!");
        device = dev;
        LOG_DEBUG("Device set!");
    }

    void HFP::HFPImpl::setOwnerService(const sys::Service *service)

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp +1 -1
@@ 25,7 25,7 @@ namespace bluetooth
        auto operator=(HFP &&other) noexcept -> HFP &;

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

        void connect() override;

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp +2 -1
@@ 24,7 24,7 @@ namespace bluetooth
        void initializeCall() const noexcept;
        void connect();
        void disconnect();
        void setDeviceAddress(bd_addr_t addr);
        void setDevice(Devicei device);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;



@@ 54,5 54,6 @@ namespace bluetooth
        static const char *call_hold_services[5];
        [[maybe_unused]] static int hf_indicators_nr;
        [[maybe_unused]] static hfp_generic_status_indicator_t hf_indicators[2];
        static Devicei device;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +8 -7
@@ 12,6 12,7 @@
#include <service-audio/AudioMessage.hpp>
#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/messages/AudioVolume.hpp>
#include <service-bluetooth/messages/Disconnect.hpp>
#include <service-cellular/service-cellular/CellularServiceAPI.hpp>
#include <service-evtmgr/Constants.hpp>
#include <service-audio/AudioServiceAPI.hpp>


@@ 46,9 47,9 @@ namespace bluetooth
        return pimpl->init();
    }

    void HSP::setDeviceAddress(uint8_t *addr)
    void HSP::setDevice(const Devicei &device)
    {
        pimpl->setDeviceAddress(addr);
        pimpl->setDevice(device);
    }

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


@@ 98,7 99,6 @@ namespace bluetooth
    HSP::~HSP() = default;

    uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
    bd_addr_t HSP::HSPImpl::deviceAddr;
    std::array<char, commandBufferLength> HSP::HSPImpl::ATcommandBuffer;
    std::array<uint8_t, serviceBufferLength> HSP::HSPImpl::serviceBuffer;
    std::unique_ptr<SCO> HSP::HSPImpl::sco;


@@ 110,6 110,7 @@ namespace bluetooth
    bool HSP::HSPImpl::callAnswered         = false;
    bool HSP::HSPImpl::isRinging            = false;
    std::shared_ptr<HSPAudioDevice> HSP::HSPImpl::audioDevice;
    Devicei HSP::HSPImpl::device;

    void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
    {


@@ 303,7 304,7 @@ namespace bluetooth
        if (isConnected) {
            disconnect();
        }
        hsp_ag_connect(deviceAddr);
        hsp_ag_connect(device.address);
    }

    void HSP::HSPImpl::disconnect()


@@ 312,10 313,10 @@ namespace bluetooth
        hsp_ag_disconnect();
    }

    void HSP::HSPImpl::setDeviceAddress(bd_addr_t addr)
    void HSP::HSPImpl::setDevice(const Devicei &dev)
    {
        bd_addr_copy(deviceAddr, addr);
        LOG_DEBUG("Address set!");
        device = dev;
        LOG_INFO("Device set!");
    }

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

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +1 -1
@@ 24,7 24,7 @@ namespace bluetooth
        auto operator=(HSP &&other) noexcept -> HSP &;

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

        void connect() override;

M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +2 -2
@@ 24,7 24,7 @@ namespace bluetooth
        void initializeCall() const noexcept;
        void connect();
        void disconnect();
        void setDeviceAddress(bd_addr_t addr);
        void setDevice(const Devicei &dev);
        void setOwnerService(const sys::Service *service);
        auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
        void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice);


@@ 42,11 42,11 @@ namespace bluetooth
        static std::unique_ptr<CellularInterface> cellularInterface;
        static std::unique_ptr<AudioInterface> audioInterface;
        static std::array<char, commandBufferLength> ATcommandBuffer;
        static bd_addr_t deviceAddr;
        static const sys::Service *ownerService;
        static bool isConnected;
        static bool callAnswered;
        static bool isRinging;
        static std::shared_ptr<HSPAudioDevice> audioDevice;
        static Devicei device;
    };
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +1 -1
@@ 17,7 17,7 @@ namespace bluetooth
      public:
        virtual ~Profile()                                                                        = default;
        virtual auto init() -> Error::Code                                                        = 0;
        virtual void setDeviceAddress(uint8_t *addr)                                              = 0;
        virtual void setDevice(const Devicei &device)                                             = 0;
        virtual void setOwnerService(const sys::Service *service)                                 = 0;
        virtual void connect()                                                                    = 0;
        virtual void start()                                                                      = 0;

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +3 -4
@@ 39,12 39,11 @@ namespace bluetooth
        return Error::Success;
    }

    auto ProfileManager::connect(bd_addr_t address) -> Error::Code
    auto ProfileManager::connect(const Devicei &device) -> Error::Code
    {
        bd_addr_copy(remoteAddr, address);
        for (auto &[profileName, ptr] : profilesList) {
            if (ptr != nullptr) {
                ptr->setDeviceAddress(remoteAddr);
                ptr->setDevice(device);
                ptr->connect();
            }
        }


@@ 117,7 116,7 @@ namespace bluetooth
        profilesList[profileType]->setAudioDevice(device);
        return switchProfile(profileType);
    }
    auto ProfileManager::isAddressActuallyUsed(bd_addr_t address) -> bool
    auto ProfileManager::isAddressActuallyUsed(const bd_addr_t address) -> bool
    {
        return !static_cast<bool>(bd_addr_cmp(address, remoteAddr));
    }

M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +2 -2
@@ 32,7 32,7 @@ namespace bluetooth

        auto init() -> Error::Code;

        auto connect(bd_addr_t address) -> Error::Code;
        auto connect(const Devicei &device) -> Error::Code;

        auto disconnect() -> Error::Code;



@@ 43,7 43,7 @@ namespace bluetooth
        auto startRinging() -> Error::Code;
        auto stopRinging() -> Error::Code;
        auto initializeCall() -> Error::Code;
        auto isAddressActuallyUsed(bd_addr_t address) -> bool;
        auto isAddressActuallyUsed(const bd_addr_t address) -> bool;

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


M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +0 -1
@@ 47,7 47,6 @@ target_compile_options(${PROJECT_NAME}
    -Wno-missing-field-initializers
    -Wno-unused-function
    -Wno-implicit-fallthrough

    # C only flags
    "$<$<COMPILE_LANGUAGE:C>:-Wno-old-style-declaration>"
)

M module-bluetooth/tests/CMakeLists.txt => module-bluetooth/tests/CMakeLists.txt +2 -1
@@ 1,9 1,10 @@
add_catch2_executable(
    NAME
        StatefulController-tests
        Bluetooth-tests
    SRCS
        tests-main.cpp
        tests-StatefulController.cpp
        tests-BluetoothDevicesModel.cpp
    LIBS
        module-sys
        module-bluetooth

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

#include <catch2/catch.hpp>
#include "Device.hpp"
#include "service-bluetooth/BluetoothDevicesModel.hpp"
TEST_CASE("Devicei comparison")
{
    Devicei device1{"Dev1"};
    Devicei device2{"Dev2"};
    Devicei device3{"Dev1"};

    bd_addr_t addr1;
    bd_addr_t addr2;

    std::string addr1Str{"00:12:6F:E7:9D:05"};
    std::string addr2Str{"F8:5C:7D:17:E4:8F"};

    sscanf_bd_addr(addr1Str.c_str(), addr1);
    sscanf_bd_addr(addr2Str.c_str(), addr2);

    SECTION("Same addresses and names")
    {
        device1.setAddress(&addr1);
        device3.setAddress(&addr1);
        REQUIRE(device1 == device3);
    }

    SECTION("Different addresses and names")
    {
        device1.setAddress(&addr1);
        device2.setAddress(&addr2);
        REQUIRE_FALSE(device1 == device3);
    }

    SECTION("Same names and different addresses")
    {
        device1.setAddress(&addr1);
        device3.setAddress(&addr2);
        REQUIRE_FALSE(device1 == device3);
    }

    SECTION("Different addresses and names - != operator")
    {
        device1.setAddress(&addr1);
        device2.setAddress(&addr2);
        REQUIRE(device1 != device3);
    }

    SECTION("Same names and different addresses - != operator")
    {
        device1.setAddress(&addr1);
        device3.setAddress(&addr2);
        REQUIRE(device1 != device3);
    }
}

TEST_CASE("Device handling")
{
    BluetoothDevicesModel devicesModel{nullptr};

    Devicei device1{"Dev1"};
    Devicei device2{"Dev2"};
    Devicei device3{"Dev1"};
    Devicei device4{"Dev4"};
    Devicei dummy{""};

    bd_addr_t addr1;
    bd_addr_t addr2;
    bd_addr_t addr3;

    std::string addr1Str{"00:12:6F:E7:9D:05"};
    std::string addr2Str{"F8:5C:7D:17:E4:8F"};
    std::string addr3Str{"F8:5C:7D:17:E4:00"};

    sscanf_bd_addr(addr1Str.c_str(), addr1);
    sscanf_bd_addr(addr2Str.c_str(), addr2);
    sscanf_bd_addr(addr3Str.c_str(), addr3);

    device1.setAddress(&addr1);
    device2.setAddress(&addr2);
    device3.setAddress(&addr1);
    device4.setAddress(&addr2);

    devicesModel.insertDevice(device1);
    devicesModel.insertDevice(device2);
    devicesModel.insertDevice(device3);

    SECTION("Add device to the list")
    {
        devicesModel.insertDevice(Devicei{"testDevice"});
        REQUIRE(devicesModel.getDevices().size() == 4);
    }

    SECTION("Merge device list")
    {
        std::vector<Devicei> devicesList, targetList;
        Devicei dummy{"dummyDevice"};
        dummy.setAddress(&addr3);
        devicesList.push_back(dummy);

        targetList.push_back(dummy);
        targetList.push_back(device1);
        targetList.push_back(device2);

        devicesModel.mergeDevicesList(devicesList);

        REQUIRE(!devicesModel.getDeviceByAddress(addr1Str).value().get().name.empty());
        REQUIRE(!devicesModel.getDeviceByAddress(addr2Str).value().get().name.empty());
        REQUIRE(!devicesModel.getDeviceByAddress(addr3Str).value().get().name.empty());
        REQUIRE(devicesModel.getDevices().size() == 3);
    }

    SECTION("Get device with wrong address")
    {
        std::string addrStr{"10:12:6F:E8:9D:05"};
        REQUIRE_FALSE(devicesModel.getDeviceByAddress(addrStr).has_value());
    }

    SECTION("Get device with correct address")
    {
        REQUIRE(devicesModel.getDeviceByAddress(addr2Str).value().get().name == "Dev2");
    }

    SECTION("Remove device from the list")
    {
        devicesModel.removeDevice(device2);
        REQUIRE(devicesModel.getDevices().size() == 2);
    }
}

M module-bluetooth/tests/tests-StatefulController.cpp => module-bluetooth/tests/tests-StatefulController.cpp +2 -2
@@ 30,11 30,11 @@ class DriverMock : public AbstractDriver
    {}
    void setVisibility(bool visibility) override
    {}
    bool pair(uint8_t *addr, std::uint8_t protectionLevel = 0) override
    bool pair(Devicei device, std::uint8_t protectionLevel = 0) override
    {
        return true;
    }
    bool unpair(uint8_t *addr) override
    bool unpair(Devicei device) override
    {
        return true;
    }

M module-services/service-bluetooth/CMakeLists.txt => module-services/service-bluetooth/CMakeLists.txt +1 -0
@@ 7,6 7,7 @@ set(SOURCES
    service-bluetooth/SettingsHolder.cpp
    service-bluetooth/SettingsSerializer.cpp
    service-bluetooth/BluetoothMessage.cpp
    service-bluetooth/BluetoothDevicesModel.cpp
)

add_library(${PROJECT_NAME} STATIC ${SOURCES})

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +83 -32
@@ 21,6 21,7 @@
#include "service-bluetooth/messages/Unpair.hpp"
#include "service-bluetooth/messages/SetDeviceName.hpp"
#include "service-bluetooth/messages/Ring.hpp"
#include "service-bluetooth/BluetoothDevicesModel.hpp"
#include "service-bluetooth/messages/BluetoothModeChanged.hpp"

#include "SystemManager/messages/SentinelRegistrationMessage.hpp"


@@ 56,15 57,16 @@ ServiceBluetooth::~ServiceBluetooth()
    LOG_INFO("[ServiceBluetooth] Cleaning resources");
}

// This code is experimental:
// this means it is an init point of bluetooth feature handling
sys::ReturnCodes ServiceBluetooth::InitHandler()
{
    auto settings = std::make_unique<settings::Settings>();
    settings->init(service::ServiceProxy(shared_from_this()));
    settingsHolder                  = std::make_shared<bluetooth::SettingsHolder>(std::move(settings));
    bluetoothDevicesModel           = std::make_shared<BluetoothDevicesModel>(this);
    bluetooth::KeyStorage::settings = settingsHolder;

    bus.channels.push_back(sys::BusChannel::BluetoothNotifications);

    worker = std::make_unique<BluetoothWorker>(this);
    worker->run();



@@ 83,6 85,7 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
    connectHandler<BluetoothAudioStartMessage>();
    connectHandler<BluetoothMessage>();
    connectHandler<BluetoothPairMessage>();
    connectHandler<BluetoothPairResultMessage>();
    connectHandler<message::bluetooth::A2DPVolume>();
    connectHandler<message::bluetooth::HSPVolume>();
    connectHandler<message::bluetooth::Ring>();


@@ 147,14 150,14 @@ auto ServiceBluetooth::handle(BluetoothAudioStartMessage *msg) -> std::shared_pt
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));
    bluetoothDevicesModel->mergeDevicesList(SettingsSerializer::fromString(bondedDevicesStr));

    return std::make_shared<message::bluetooth::ResponseBondedDevices>(SettingsSerializer::fromString(bondedDevicesStr),
                                                                       std::move(connectedAddress));
    bus.sendMulticast(
        std::make_shared<message::bluetooth::ResponseBondedDevices>(bluetoothDevicesModel->getDevices(), ""),
        sys::BusChannel::BluetoothNotifications);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestStatus *msg) -> std::shared_ptr<sys::Message>


@@ 163,7 166,9 @@ auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestStatus
    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);
    bus.sendMulticast(std::make_shared<message::bluetooth::ResponseStatus>(status),
                      sys::BusChannel::BluetoothNotifications);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared_ptr<sys::Message>


@@ 177,6 182,12 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared
        bus.sendMulticast(
            std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Enabled),
            sys::BusChannel::BluetoothModeChanges);
        {
            auto bondedDevicesStr = std::visit(bluetooth::StringVisitor(),
                                               this->settingsHolder->getValue(bluetooth::Settings::BondedDevices));
            bluetoothDevicesModel->mergeDevicesList(SettingsSerializer::fromString(bondedDevicesStr));
            bluetoothDevicesModel->syncDevicesWithApp();
        }
        break;
    case BluetoothStatus::State::Off:
        sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::PowerOff));


@@ 196,19 207,41 @@ auto ServiceBluetooth::handle(message::bluetooth::SetStatus *msg) -> std::shared

auto ServiceBluetooth::handle(BluetoothPairMessage *msg) -> std::shared_ptr<sys::Message>
{
    const auto addrString = msg->addr;
    bd_addr_t addr;
    sscanf_bd_addr(addrString.c_str(), addr);
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, addr));
    auto device = msg->getDevice();
    bluetoothDevicesModel->removeDevice(device);
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Pair, device));

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

    bluetoothDevicesModel->syncDevicesWithApp();

    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(BluetoothPairResultMessage *msg) -> std::shared_ptr<sys::Message>
{
    auto device = msg->getDevice();
    if (msg->isSucceed()) {
        bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Paired);
    }
    else {
        bluetoothDevicesModel->removeDevice(device);
    }

    bluetoothDevicesModel->syncDevicesWithApp();

    bus.sendMulticast(std::make_shared<BluetoothPairResultMessage>(msg->getDevice(), msg->isSucceed()),
                      sys::BusChannel::BluetoothNotifications);

    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));
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::Unpair, msg->getDevice()));
    bluetoothDevicesModel->removeDevice(msg->getDevice());

    return sys::MessageNone{};
}



@@ 218,7 251,9 @@ auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::RequestDevice
    auto deviceNameString =
        std::visit(bluetooth::StringVisitor(), this->settingsHolder->getValue(bluetooth::Settings::DeviceName));

    return std::make_shared<message::bluetooth::ResponseDeviceName>(std::move(deviceNameString));
    bus.sendMulticast(std::make_shared<message::bluetooth::ResponseDeviceName>(std::move(deviceNameString)),
                      sys::BusChannel::BluetoothNotifications);
    return sys::MessageNone{};
}

auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::shared_ptr<sys::Message>


@@ 233,24 268,36 @@ auto ServiceBluetooth::handle(message::bluetooth::SetDeviceName *msg) -> std::sh

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));
    auto device = msg->getDevice();
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, device));

    bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Connecting);
    bluetoothDevicesModel->syncDevicesWithApp();
    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());
        auto device = msg->getDevice();
        bluetoothDevicesModel->setInternalDeviceState(device, DeviceState::Connected);

        settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, bd_addr_to_str(device.address));
        startTimeoutTimer();
        bus.sendMulticast(
            std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Connected),
            sys::BusChannel::BluetoothModeChanges);
    }
    bus.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(*msg), nameSettings);

    for (auto &device : bluetoothDevicesModel->getDevices()) {
        if (device.deviceState == DeviceState::Connecting) {
            device.deviceState = DeviceState::Paired;
        }
    }

    bluetoothDevicesModel->syncDevicesWithApp();
    bus.sendMulticast(std::make_shared<message::bluetooth::ConnectResult>(*msg),
                      sys::BusChannel::BluetoothNotifications);
    return sys::MessageNone{};
}



@@ 262,14 309,20 @@ auto ServiceBluetooth::handle([[maybe_unused]] message::bluetooth::Disconnect *m

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

    auto device = bluetoothDevicesModel->getDeviceByAddress(deviceAddr);
    if (device.has_value()) {
        device.value().get().deviceState = DeviceState::Paired;
    }
    bluetoothDevicesModel->syncDevicesWithApp();
    settingsHolder->setValue(bluetooth::Settings::ConnectedDevice, std::string());
    bus.sendMulticast(std::make_shared<sys::bluetooth::BluetoothModeChanged>(sys::bluetooth::BluetoothMode::Enabled),
                      sys::BusChannel::BluetoothModeChanges);
    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()),
                    nameSettings);

    bus.sendMulticast(std::make_shared<message::bluetooth::DisconnectResult>(msg->getDevice()),
                      sys::BusChannel::BluetoothNotifications);
    stopTimeoutTimer();

    return sys::MessageNone{};


@@ 342,9 395,7 @@ auto ServiceBluetooth::handle(BluetoothMessage *msg) -> std::shared_ptr<sys::Mes

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));
    sendWorkerCommand(bluetooth::Command(bluetooth::Command::Type::ConnectAudio, msg->device));
    return std::make_shared<sys::ResponseMessage>();
}


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

#include "BluetoothDevicesModel.hpp"
#include <service-bluetooth/messages/BondedDevices.hpp>

BluetoothDevicesModel::BluetoothDevicesModel(sys::Service *service) : service{service}
{}

void BluetoothDevicesModel::mergeDevicesList(const std::vector<Devicei> &devicesList)
{
    devices.insert(std::end(devices), std::begin(devicesList), std::end(devicesList));

    // remove duplicates
    auto end = std::end(devices);
    for (auto it = std::begin(devices); it != end; ++it) {
        end = std::remove(it + 1, end, *it);
    }
    devices.erase(end, std::end(devices));
}

void BluetoothDevicesModel::insertDevice(const Devicei &device)
{
    devices.emplace_back(device);
    syncDevicesWithApp();
}
auto BluetoothDevicesModel::getDeviceByAddress(const std::string &address)
    -> std::optional<std::reference_wrapper<Devicei>>
{

    auto deviceIt = std::find_if(std::begin(devices), std::end(devices), [address](const Devicei &device) {
        return bd_addr_to_str(device.address) == address;
    });

    if (deviceIt == std::end(devices)) {
        return std::nullopt;
    }
    return std::ref(*deviceIt);
}
auto BluetoothDevicesModel::getDeviceByAddress(const bd_addr_t address)
    -> std::optional<std::reference_wrapper<Devicei>>
{

    auto deviceIt = std::find_if(std::begin(devices), std::end(devices), [address](const Devicei &device) {
        return std::memcmp(address, device.address, sizeof(bd_addr_t)) == 0;
    });

    if (deviceIt == std::end(devices)) {
        return std::nullopt;
    }
    return std::ref(*deviceIt);
}
void BluetoothDevicesModel::removeDevice(const Devicei &device)
{
    auto position = std::find(std::begin(devices), std::end(devices), device);
    if (position != std::end(devices)) {
        devices.erase(position);
    }
    syncDevicesWithApp();
}
auto BluetoothDevicesModel::getDevices() -> std::vector<Devicei> &
{
    return devices;
}
void BluetoothDevicesModel::syncDevicesWithApp()
{
    if (service != nullptr) {
        service->bus.sendMulticast(std::make_shared<::message::bluetooth::SyncDevices>(devices),
                                   sys::BusChannel::BluetoothNotifications);
    }
}
void BluetoothDevicesModel::setInternalDeviceState(const Devicei &device, const DeviceState &state)
{
    auto dev                      = getDeviceByAddress(device.address);
    dev.value().get().deviceState = state;
}

A module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.hpp +31 -0
@@ 0,0 1,31 @@
// 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 "Application.hpp"
#include <Device.hpp>
#include "service-bluetooth/messages/SyncDevices.hpp"
extern "C"
{
#include <module-bluetooth/lib/btstack/src/btstack_util.h>
}

class BluetoothDevicesModel
{
  public:
    explicit BluetoothDevicesModel(sys::Service *service);

    void mergeDevicesList(const std::vector<Devicei> &devicesList);
    auto getDeviceByAddress(const std::string &address) -> std::optional<std::reference_wrapper<Devicei>>;
    auto getDeviceByAddress(const bd_addr_t address) -> std::optional<std::reference_wrapper<Devicei>>;
    void insertDevice(const Devicei &device);
    void removeDevice(const Devicei &device);
    auto getDevices() -> std::vector<Devicei> &;
    void syncDevicesWithApp();
    void setInternalDeviceState(const Devicei &device, const DeviceState &state);

  private:
    std::vector<Devicei> devices{};
    sys::Service *service = nullptr;
};

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.cpp +8 -5
@@ 9,10 9,13 @@ extern "C"
}
#include <utility>

BluetoothAddrMessage::BluetoothAddrMessage(std::string addr) : sys::DataMessage(MessageType::BluetoothAddrResult)
BluetoothAddrMessage::BluetoothAddrMessage(Devicei device)
    : sys::DataMessage(MessageType::BluetoothAddrResult), device(device)
{}
BluetoothPairMessage::BluetoothPairMessage(Devicei device)
    : sys::DataMessage(MessageType::BluetoothPairResult), device(device)
{}
auto BluetoothPairMessage::getDevice() const noexcept -> Devicei
{
    sscanf_bd_addr(addr.c_str(), this->addr);
    return device;
}
BluetoothPairMessage::BluetoothPairMessage(std::string addr)
    : sys::DataMessage(MessageType::BluetoothPairResult), addr(std::move(addr))
{}

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +10 -10
@@ 51,12 51,12 @@ class BluetoothMessage : public sys::DataMessage
class BluetoothPairResultMessage : public sys::DataMessage
{
  public:
    explicit BluetoothPairResultMessage(std::string addr, bool succeed)
        : sys::DataMessage(MessageType::BluetoothPairResult), addr(std::move(addr)), succeed(succeed)
    explicit BluetoothPairResultMessage(const Devicei device, bool succeed)
        : sys::DataMessage(MessageType::BluetoothPairResult), device(device), succeed(succeed)
    {}
    [[nodiscard]] auto getAddr() const -> std::string
    [[nodiscard]] auto getDevice() const -> Devicei
    {
        return addr;
        return device;
    }
    [[nodiscard]] auto isSucceed() const noexcept -> bool
    {


@@ 64,24 64,24 @@ class BluetoothPairResultMessage : public sys::DataMessage
    }

  private:
    std::string addr;
    std::string name;
    Devicei device;
    bool succeed;
};

class BluetoothAddrMessage : public sys::DataMessage
{
  public:
    bd_addr_t addr;
    explicit BluetoothAddrMessage(std::string addr);
    Devicei device;
    explicit BluetoothAddrMessage(Devicei device);
    ~BluetoothAddrMessage() override = default;
};

class BluetoothPairMessage : public sys::DataMessage
{
  public:
    std::string addr;
    explicit BluetoothPairMessage(std::string addr);
    Devicei device;
    explicit BluetoothPairMessage(Devicei device);
    [[nodiscard]] auto getDevice() const noexcept -> Devicei;
    ~BluetoothPairMessage() override = default;
};


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

#include "BluetoothSettingsModel.hpp"

#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/messages/BondedDevices.hpp>
#include <service-bluetooth/messages/Connect.hpp>
#include <service-bluetooth/messages/DeviceName.hpp>
#include <service-bluetooth/messages/Disconnect.hpp>
#include <service-bluetooth/messages/Status.hpp>
#include <service-bluetooth/messages/SetStatus.hpp>
#include <service-bluetooth/messages/SetDeviceName.hpp>
#include <service-bluetooth/messages/Passkey.hpp>
#include <service-bluetooth/messages/Unpair.hpp>

BluetoothSettingsModel::BluetoothSettingsModel(app::Application *application) : application{application}
{}

void BluetoothSettingsModel::requestStatus()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestStatus>(), service::name::bluetooth);
}

void BluetoothSettingsModel::setStatus(const bool desiredBluetoothState, const bool desiredVisibility)
{
    BluetoothStatus status{.state = desiredBluetoothState ? BluetoothStatus::State::On : BluetoothStatus::State::Off,
                           .visibility = desiredVisibility};
    message::bluetooth::SetStatus setStatus(status);
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::SetStatus>(std::move(setStatus)),
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::requestDeviceName()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestDeviceName>(), service::name::bluetooth);
}

void BluetoothSettingsModel::setDeviceName(const UTF8 &deviceName)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::SetDeviceName>(deviceName),
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::requestBondedDevices()
{
    application->bus.sendUnicast(std::make_shared<::message::bluetooth::RequestBondedDevices>(),
                                 service::name::bluetooth);
}

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

void BluetoothSettingsModel::stopScan()
{
    application->bus.sendUnicast(std::make_shared<BluetoothMessage>(BluetoothMessage::Request::StopScan),
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::requestDevicePair(const std::string &addr)
{
    application->bus.sendUnicast(std::make_shared<BluetoothPairMessage>(addr), service::name::bluetooth);
}

void BluetoothSettingsModel::requestDeviceUnpair(const Devicei &device)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Unpair>(bd_addr_to_str(device.address)),
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::responsePasskey(const std::string &passkey)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::ResponsePasskey>(passkey),
                                 service::name::bluetooth);
}

void BluetoothSettingsModel::requestConnection(const std::string &addr)
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Connect>(addr), service::name::bluetooth);
}

void BluetoothSettingsModel::requestDisconnection()
{
    application->bus.sendUnicast(std::make_shared<message::bluetooth::Disconnect>(), service::name::bluetooth);
}
void BluetoothSettingsModel::mergeDevicesList(const std::vector<Devicei> &devicesList)
{
    devices.insert(std::end(devices), std::begin(devicesList), std::end(devicesList));

    // remove duplicates
    auto end = std::end(devices);
    for (auto it = std::begin(devices); it != end; ++it) {
        end = std::remove(it + 1, end, *it);
    }
    devices.erase(end, std::end(devices));
}
void BluetoothSettingsModel::setActiveDeviceState(const DeviceState &state)
{
    auto activeDevice = getActiveDevice();
    if (activeDevice.has_value()) {
        activeDevice.value().get().deviceState = state;
    }
}
auto BluetoothSettingsModel::getActiveDevice() -> std::optional<std::reference_wrapper<Devicei>>
{
    try {
        return devices.at(activeDeviceIndex);
    }
    catch (const std::out_of_range &oor) {
        LOG_FATAL("NO DEVICE FOUND!");
        return std::nullopt;
    }
}
auto BluetoothSettingsModel::getSelectedDevice() -> std::optional<std::reference_wrapper<Devicei>>
{
    try {
        return devices.at(selectedDeviceIndex);
    }
    catch (const std::out_of_range &oor) {
        LOG_FATAL("NO DEVICE FOUND!");
        return std::nullopt;
    }
}
void BluetoothSettingsModel::setActiveDevice(const Devicei &device)
{
    auto itr          = std::find(std::begin(devices), std::end(devices), device);
    activeDeviceIndex = std::distance(std::begin(devices), itr);
}
void BluetoothSettingsModel::setSelectedDevice(const Devicei &device)
{
    auto itr            = std::find(std::begin(devices), std::end(devices), device);
    selectedDeviceIndex = std::distance(std::begin(devices), itr);
}
void BluetoothSettingsModel::insertDevice(const Devicei device)
{
    devices.emplace_back(device);
}
auto BluetoothSettingsModel::getDeviceByAddress(const std::string &address)
    -> std::optional<std::reference_wrapper<Devicei>>
{
    auto deviceIt = std::find_if(std::begin(devices), std::end(devices), [address](const Devicei &device) {
        return bd_addr_to_str(device.address) == address;
    });

    if (deviceIt == std::end(devices)) {
        return std::nullopt;
    }
    return std::ref(*deviceIt);
}
void BluetoothSettingsModel::removeDevice(const Devicei &device)
{
    devices.erase(std::remove(std::begin(devices), std::end(devices), device), std::end(devices));
}
auto BluetoothSettingsModel::getDevices() -> std::vector<Devicei> &
{
    return devices;
}
auto BluetoothSettingsModel::isDeviceConnecting() -> bool
{

    auto deviceIt = std::find_if(std::begin(devices), std::end(devices), [](const Devicei &device) {
        return device.deviceState == DeviceState::Connecting;
    });

    if (deviceIt != std::end(devices)) {
        return true;
    }
    return false;
}

A module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.hpp +59 -0
@@ 0,0 1,59 @@
// 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 "Application.hpp"
#include <Device.hpp>

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

class ActiveDevice
{
  public:
    explicit ActiveDevice(std::string address) : address(std::move(address))
    {}
    ActiveDevice()    = default;
    DeviceState state = DeviceState::Unknown;
    std::string address;
};

class BluetoothSettingsModel
{
  public:
    explicit BluetoothSettingsModel(app::Application *application);

    void requestStatus();
    void setStatus(bool desiredBluetoothState, bool desiredVisibility);
    void requestDeviceName();
    void setDeviceName(const UTF8 &deviceName);
    void requestBondedDevices();
    void requestScan();
    void stopScan();
    void requestDevicePair(const std::string &addr);
    void requestDeviceUnpair(const Devicei &device);
    void responsePasskey(const std::string &passkey);
    void requestConnection(const std::string &addr);
    void requestDisconnection();
    void replaceDevicesList(const std::vector<Devicei> &devicesList);
    void setActiveDeviceState(const DeviceState &state);
    auto getActiveDevice() -> std::optional<std::reference_wrapper<Devicei>>;
    auto getSelectedDevice() -> std::optional<std::reference_wrapper<Devicei>>;
    auto getDeviceByAddress(const std::string &address) -> std::optional<std::reference_wrapper<Devicei>>;
    void setActiveDevice(const Devicei &device);
    void setSelectedDevice(const Devicei &device);
    void insertDevice(Devicei device);
    void removeDevice(const Devicei &device);
    auto getDevices() -> std::vector<Devicei> &;
    auto isDeviceConnecting() -> bool;

  private:
    std::vector<Devicei> devices{};
    std::uint16_t activeDeviceIndex   = 0;
    std::uint16_t selectedDeviceIndex = 0;
    app::Application *application     = nullptr;
    Devicei dummyDevice{""};
};

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +5 -0
@@ 12,6 12,7 @@
#include <service-audio/ServiceAudio.hpp>
#include <module-bluetooth/Bluetooth/CommandHandler.hpp>
#include "ProfileManager.hpp"
#include "BluetoothDevicesModel.hpp"
#include <Service/CpuSentinel.hpp>
#include <Timers/TimerHandle.hpp>



@@ 52,6 53,7 @@ namespace message::bluetooth
    class HSPVolume;
    class Ring;
    class StartAudioRouting;
    class GetBluetoothDevicesModel;
} // namespace message::bluetooth

class ServiceBluetooth : public sys::Service


@@ 75,6 77,7 @@ class ServiceBluetooth : public sys::Service
    std::unique_ptr<BluetoothWorker> worker;
    std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    sys::TimerHandle connectionTimeoutTimer;
    std::shared_ptr<BluetoothDevicesModel> bluetoothDevicesModel{};

    void startTimeoutTimer();
    void stopTimeoutTimer();


@@ 88,6 91,7 @@ class ServiceBluetooth : public sys::Service
    [[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(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>;


@@ 104,6 108,7 @@ class ServiceBluetooth : public sys::Service
    [[nodiscard]] auto handle(message::bluetooth::Ring *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::StartAudioRouting *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::ResponsePasskey *msg) -> std::shared_ptr<sys::Message>;
    [[nodiscard]] auto handle(message::bluetooth::GetBluetoothDevicesModel *msg) -> std::shared_ptr<sys::Message>;
};

namespace sys

M module-services/service-bluetooth/service-bluetooth/SettingsSerializer.cpp => module-services/service-bluetooth/service-bluetooth/SettingsSerializer.cpp +1 -0
@@ 41,6 41,7 @@ auto SettingsSerializer::fromString(const std::string &jsonStr) -> std::vector<D
        Devicei temp;
        sscanf_bd_addr(device[strings::addr].string_value().c_str(), temp.address);
        temp.name = device[strings::name].string_value();
        temp.deviceState = DeviceState::Paired;
        devicesVector.emplace_back(temp);
    }
    return devicesVector;

M module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp => module-services/service-bluetooth/service-bluetooth/messages/Connect.hpp +8 -8
@@ 10,25 10,25 @@ namespace message::bluetooth
    class Connect : public BluetoothMessage
    {
      public:
        explicit Connect(std::string addr) : addr(std::move(addr))
        explicit Connect(Devicei device) : device(std::move(device))
        {}
        [[nodiscard]] auto getAddr() const -> std::string
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return addr;
            return device;
        }

      private:
        std::string addr;
        Devicei device;
    };

    class ConnectResult : public BluetoothMessage
    {
      public:
        explicit ConnectResult(std::string addr, bool succeed) : addr(std::move(addr)), succeed(succeed)
        explicit ConnectResult(Devicei device, bool succeed) : device(device), succeed(succeed)
        {}
        [[nodiscard]] auto getAddr() const -> std::string
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return addr;
            return device;
        }
        [[nodiscard]] auto isSucceed() const noexcept -> bool
        {


@@ 36,7 36,7 @@ namespace message::bluetooth
        }

      private:
        std::string addr;
        Devicei device;
        bool succeed;
    };
} // namespace message::bluetooth

M module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp => module-services/service-bluetooth/service-bluetooth/messages/Disconnect.hpp +12 -1
@@ 11,5 11,16 @@ namespace message::bluetooth
    {};

    class DisconnectResult : public BluetoothMessage
    {};
    {
      public:
        explicit DisconnectResult(Devicei device) : device(device)
        {}
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return device;
        }

      private:
        Devicei device;
    };
} // namespace message::bluetooth

M module-services/service-bluetooth/service-bluetooth/messages/Status.hpp => module-services/service-bluetooth/service-bluetooth/messages/Status.hpp +21 -1
@@ 4,7 4,7 @@
#pragma once

#include "service-bluetooth/BluetoothMessage.hpp"

#include <Bluetooth/interface/profiles/AudioProfile.hpp>
namespace message::bluetooth
{



@@ 24,4 24,24 @@ namespace message::bluetooth
      private:
        BluetoothStatus status;
    };

    class ProfileStatus : public BluetoothMessage
    {
      public:
        ProfileStatus(::bluetooth::AudioProfile profile, bool isConnected) : isConnected(isConnected), profile(profile)
        {}

        [[nodiscard]] auto getProfile() const noexcept -> ::bluetooth::AudioProfile
        {
            return profile;
        }
        [[nodiscard]] auto isProfileConnected() const noexcept -> bool
        {
            return isConnected;
        }

      private:
        bool isConnected;
        ::bluetooth::AudioProfile profile;
    };
} // namespace message::bluetooth

A module-services/service-bluetooth/service-bluetooth/messages/SyncDevices.hpp => module-services/service-bluetooth/service-bluetooth/messages/SyncDevices.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-bluetooth/BluetoothMessage.hpp"

namespace message::bluetooth
{
    class SyncDevices : public BluetoothMessage
    {
      public:
        explicit SyncDevices(std::vector<Devicei> devices) : devices(std::move(devices))
        {}
        [[nodiscard]] auto getDevices() const noexcept -> std::vector<Devicei>
        {
            return devices;
        }

      private:
        std::vector<Devicei> devices;
    };
} // namespace message::bluetooth

M module-services/service-bluetooth/service-bluetooth/messages/Unpair.hpp => module-services/service-bluetooth/service-bluetooth/messages/Unpair.hpp +8 -8
@@ 10,25 10,25 @@ namespace message::bluetooth
    class Unpair : public BluetoothMessage
    {
      public:
        explicit Unpair(std::string addr) : addr(std::move(addr))
        explicit Unpair(Devicei device) : device(device)
        {}
        [[nodiscard]] auto getAddr() const -> std::string
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return addr;
            return device;
        }

      private:
        std::string addr;
        Devicei device;
    };

    class UnpairResult : public BluetoothMessage
    {
      public:
        explicit UnpairResult(std::string addr, bool succeed) : addr(std::move(addr)), succeed(succeed)
        explicit UnpairResult(Devicei device, bool succeed) : device(device), succeed(succeed)
        {}
        [[nodiscard]] auto getAddr() const -> std::string
        [[nodiscard]] auto getDevice() const -> Devicei
        {
            return addr;
            return device;
        }
        [[nodiscard]] auto isSucceed() const noexcept -> bool
        {


@@ 36,7 36,7 @@ namespace message::bluetooth
        }

      private:
        std::string addr;
        Devicei device;
        bool succeed;
    };
} // namespace message::bluetooth

M module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp => module-services/service-desktop/endpoints/bluetooth/BluetoothHelper.cpp +9 -3
@@ 79,11 79,15 @@ auto parserFSM::BluetoothHelper::processPostRequest(Context &context) -> sys::Re
    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));
        auto device = Devicei("DevMode device");
        sscanf_bd_addr(address.c_str(), device.address);
        msg = std::make_unique<::message::bluetooth::Connect>(device);
    }
    else if (auto address = body[json::bluetooth::pair].string_value(); !address.empty()) {
        auto device = Devicei("DevMode device");
        sscanf_bd_addr(address.c_str(), device.address);
        LOG_INFO("Requesting pairing form harness");
        msg = std::make_unique<BluetoothPairMessage>(std::move(address));
        msg = std::make_unique<BluetoothPairMessage>(device);
    }

    sendRequest(context, std::move(msg));


@@ 102,7 106,9 @@ auto parserFSM::BluetoothHelper::processDeleteRequest(Context &context) -> sys::
    }
    else if (auto address = body[json::bluetooth::unpair].string_value(); !address.empty()) {
        LOG_INFO("Requesting pairing form harness");
        msg = std::make_unique<::message::bluetooth::Unpair>(std::move(address));
        auto device = Devicei("DevMode device");
        sscanf_bd_addr(address.c_str(), device.address);
        msg = std::make_unique<::message::bluetooth::Unpair>(device);
    }
    sendRequest(context, std::move(msg));
    MessageHandler::putToSendQueue(context.createSimpleResponse());

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +18 -0
@@ 29,6 29,8 @@
#include <service-desktop/DesktopMessages.hpp>
#include <service-time/Constants.hpp>
#include <service-time/service-time/TimeMessage.hpp>
#include <service-bluetooth/messages/Status.hpp>
#include <application-settings/ApplicationSettings.hpp>

#include <cassert>
#include <fstream>


@@ 40,6 42,7 @@
#include <EventStore.hpp>
#include <ticks.hpp>
#include <purefs/filesystem_paths.hpp>
#include <Constants.hpp>

namespace
{


@@ 91,6 94,21 @@ sys::MessagePointer EventManagerCommon::DataReceivedHandler(sys::DataMessage *ms
        handled = true;
    }
    else if (auto msg = dynamic_cast<AudioEventRequest *>(msgl); msg) {
        auto event = msg->getEvent();
        switch (event->getType()) {
        case audio::EventType::BlutoothA2DPDeviceState: {
            auto message = std::make_shared<message::bluetooth::ProfileStatus>(
                bluetooth::AudioProfile::A2DP, (event->getDeviceState() == audio::Event::DeviceState::Connected));
            bus.sendUnicast(message, app::name_settings);
        } break;
        case audio::EventType::BlutoothHSPDeviceState: {
            auto message = std::make_shared<message::bluetooth::ProfileStatus>(
                bluetooth::AudioProfile::HSP, (event->getDeviceState() == audio::Event::DeviceState::Connected));
            bus.sendUnicast(message, app::name_settings);
        } break;
        default:
            break;
        }
        AudioServiceAPI::SendEvent(this, msg->getEvent());
        handled = true;
    }

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +4 -1
@@ 25,7 25,8 @@ namespace sys
        PhoneModeChanges,
        PhoneLockChanges,
        AlarmChanges,
        BluetoothModeChanges
        BluetoothModeChanges,
        BluetoothNotifications
    };

    enum class ServicePriority


@@ 123,6 124,8 @@ inline const char *c_str(sys::BusChannel channel)
        return "PhoneModeChanges";
    case sys::BusChannel::BluetoothModeChanges:
        return "BluetoothModeChanges";
    case sys::BusChannel::BluetoothNotifications:
        return "BluetoothNotifications";
    case sys::BusChannel::PhoneLockChanges:
        return "PhoneLockChanges";
    case sys::BusChannel::AlarmChanges:

M products/PurePhone/services/appmgr/ApplicationManager.cpp => products/PurePhone/services/appmgr/ApplicationManager.cpp +1 -0
@@ 48,6 48,7 @@ namespace app::manager
            this, autoLockTimerName, sys::timer::InfiniteTimeout, [this](sys::Timer &) { onPhoneLocked(); });

        bus.channels.push_back(sys::BusChannel::BluetoothModeChanges);
        bus.channels.push_back(sys::BusChannel::BluetoothNotifications);
        bus.channels.push_back(sys::BusChannel::PhoneModeChanges);
        bus.channels.push_back(sys::BusChannel::PhoneLockChanges);
        bus.channels.push_back(sys::BusChannel::ServiceCellularNotifications);