~aleteoryx/muditaos

76c924fd5ee9b3d99ba61d40bd5fa932e40715c0 — Adam Dobrowolski 4 years ago 3bfd6bc
[EGD-8002] Added SSP, fixed some minor issues, removed obsolete code

ssp seems working
minor crash fixups
copy lacks patched
obsolete code removed
Devicei tests added for added code and cleanup
Added missing tests for new functions
Code cleaned up a bit - mostly moved to cpp
29 files changed, 755 insertions(+), 380 deletions(-)

M .gdbinit-1051
M module-apps/application-settings/ApplicationSettings.cpp
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/BluetoothCheckPasskeyWindow.cpp
M module-apps/application-settings/windows/bluetooth/BluetoothCheckPasskeyWindow.hpp
M module-bluetooth/Bluetooth/BtKeysStorage.cpp
A module-bluetooth/Bluetooth/Device.cpp
M module-bluetooth/Bluetooth/Device.hpp
A module-bluetooth/Bluetooth/error_bluetooth.cpp
A module-bluetooth/Bluetooth/error_bluetooth.hpp
M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp
A module-bluetooth/Bluetooth/interface/profiles/GAP/Devices.cpp
A module-bluetooth/Bluetooth/interface/profiles/GAP/Devices.hpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp
A module-bluetooth/Bluetooth/interface/profiles/GAP/used_events.cpp
A module-bluetooth/Bluetooth/interface/profiles/GAP/used_events.hpp
M module-bluetooth/CMakeLists.txt
M module-bluetooth/tests/CMakeLists.txt
A module-bluetooth/tests/test-Devicei.cpp
M module-bluetooth/tests/tests-BluetoothDevicesModel.cpp
A module-bluetooth/tests/tests-Devicei.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.cpp
D module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.cpp
D module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.hpp
M module-services/service-bluetooth/service-bluetooth/messages/Passkey.hpp
M module-utils/log/Logger.cpp
M .gdbinit-1051 => .gdbinit-1051 +1 -1
@@ 13,4 13,4 @@ info threads
thread 2
tb main
b HardFault_Handler

b _exit

M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +6 -4
@@ 226,6 226,8 @@ namespace app
        });

        connect(typeid(::message::bluetooth::RequestPasskey), [&](sys::Message *msg) {
            auto m                               = dynamic_cast<::message::bluetooth::RequestPasskey *>(msg);
            bluetoothSettingsModel->pinRequestor = m->getDevice();
            switchWindow(gui::window::name::bluetooth_check_passkey);
            return sys::MessageNone{};
        });


@@ 396,10 398,10 @@ namespace app
        windowsFactory.attach(gui::window::name::phone_name, [this](ApplicationCommon *app, const std::string &name) {
            return std::make_unique<gui::PhoneNameWindow>(app, bluetoothSettingsModel);
        });
        windowsFactory.attach(gui::window::name::bluetooth_check_passkey,
                              [](ApplicationCommon *app, const std::string &name) {
                                  return std::make_unique<gui::BluetoothCheckPasskeyWindow>(app);
                              });
        windowsFactory.attach(
            gui::window::name::bluetooth_check_passkey, [this](ApplicationCommon *app, const std::string &name) {
                return std::make_unique<gui::BluetoothCheckPasskeyWindow>(app, bluetoothSettingsModel);
            });

        // Network
        windowsFactory.attach(gui::window::name::network, [](ApplicationCommon *app, const std::string &name) {

M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.cpp +2 -1
@@ 70,7 70,8 @@ void BluetoothSettingsModel::requestDeviceUnpair(const Devicei &device)

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

void BluetoothSettingsModel::requestConnection(const Devicei &device)

M module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.hpp => module-apps/application-settings/models/bluetooth/BluetoothSettingsModel.hpp +1 -0
@@ 40,6 40,7 @@ class BluetoothSettingsModel
    auto isDeviceConnecting() -> bool;
    auto getStatus() const -> const BluetoothStatus;
    auto isDeviceListEmpty() const -> bool;
    Devicei pinRequestor;

  private:
    std::vector<Devicei> devices{};

M module-apps/application-settings/windows/bluetooth/BluetoothCheckPasskeyWindow.cpp => module-apps/application-settings/windows/bluetooth/BluetoothCheckPasskeyWindow.cpp +5 -3
@@ 6,6 6,7 @@
#include <application-settings/widgets/SettingsStyle.hpp>
#include <application-settings/windows/WindowNames.hpp>

#include <utility>
#include <widgets/text/Text.hpp>

namespace gui


@@ 17,10 18,11 @@ namespace gui
    } // namespace
    namespace passkey_style = style::settings::window::bluetooth::passkey;

    BluetoothCheckPasskeyWindow::BluetoothCheckPasskeyWindow(app::ApplicationCommon *app)
        : AppWindow(app, window::name::bluetooth_check_passkey)
    BluetoothCheckPasskeyWindow::BluetoothCheckPasskeyWindow(
        app::ApplicationCommon *app, std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel)
        : AppWindow(app, window::name::bluetooth_check_passkey),
          bluetoothSettingsModel(std::move(bluetoothSettingsModel))
    {
        bluetoothSettingsModel = std::make_unique<BluetoothSettingsModel>(application);
        buildInterface();
    }


M module-apps/application-settings/windows/bluetooth/BluetoothCheckPasskeyWindow.hpp => module-apps/application-settings/windows/bluetooth/BluetoothCheckPasskeyWindow.hpp +3 -2
@@ 13,7 13,8 @@ namespace gui
    class BluetoothCheckPasskeyWindow : public AppWindow
    {
      public:
        explicit BluetoothCheckPasskeyWindow(app::ApplicationCommon *app);
        BluetoothCheckPasskeyWindow(app::ApplicationCommon *app,
                                    std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel);

      private:
        void buildInterface() override;


@@ 22,6 23,6 @@ namespace gui
        Image *image = nullptr;
        Label *label = nullptr;
        Text *text   = nullptr;
        std::unique_ptr<BluetoothSettingsModel> bluetoothSettingsModel;
        std::shared_ptr<BluetoothSettingsModel> bluetoothSettingsModel;
    };
} // namespace gui

M module-bluetooth/Bluetooth/BtKeysStorage.cpp => module-bluetooth/Bluetooth/BtKeysStorage.cpp +0 -1
@@ 59,7 59,6 @@ namespace bluetooth
        }

        keys = std::move(keysJson[strings::keys].array_items());
        LOG_INFO("Imported keys: %d", static_cast<unsigned int>(keys.size()));
    }

    void KeyStorage::closeStorage()

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

#include "Device.hpp"
#include <btstack_util.h>

Devicei::Devicei(std::string name) : Device(std::move(name))
{
    pageScanRepetitionMode = {};
    clockOffset            = {};
    classOfDevice          = {};
    state                  = DEVICE_STATE::REMOTE_NAME_FAILURE;
    deviceState            = DeviceState::Unknown;
    isPairingSSP           = false;

    memset(address, 0, sizeof(address));
}

Devicei::Devicei(bd_addr_t &address) : Devicei()
{
    setAddress(&address);
}

Devicei &Devicei::operator=(const Devicei &d)
{
    if (&d == this) {
        return *this;
    }
    setAddress(&d.address);
    setName(d.name.data());
    pageScanRepetitionMode = d.pageScanRepetitionMode;
    clockOffset            = d.clockOffset;
    classOfDevice          = d.classOfDevice;
    state                  = d.state;
    deviceState            = d.deviceState;
    isPairingSSP           = d.isPairingSSP;
    return *this;
}

Devicei::Devicei(const Devicei &d) : Devicei(d.name.data())
{
    operator=(d);
}

Devicei &Devicei::operator=(Devicei &&d) noexcept
{
    setAddress(&d.address);
    setName(d.name.data());
    pageScanRepetitionMode = d.pageScanRepetitionMode;
    clockOffset            = d.clockOffset;
    classOfDevice          = d.classOfDevice;
    state                  = d.state;
    deviceState            = d.deviceState;
    isPairingSSP           = d.isPairingSSP;
    return *this;
}

Devicei::Devicei(Devicei &&d) noexcept : Devicei(d.name.data())
{
    operator=(d);
}

void Devicei::setAddress(bd_addr_t *addr)
{
    memcpy(&address, addr, sizeof address);
}

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

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

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

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



@@ 17,20 16,25 @@ struct Device
    static constexpr auto NameBufferSize = 240;
    explicit Device(std::string name = "")
    {
        setName(name);
    }
    virtual ~Device() = default;
    std::array<char, NameBufferSize> name;
    void setName(const std::string &name)
    {
        if (name.size() > NameBufferSize) {
            throw std::runtime_error("Requested name is bigger than buffer size");
        }
        strcpy(this->name.data(), name.c_str());
    }
    virtual ~Device() = default;
    std::array<char, NameBufferSize> name;
};

enum DEVICE_STATE
{
    REMOTE_NAME_REQUEST,
    REMOTE_NAME_INQUIRED,
    REMOTE_NAME_FETCHED
    REMOTE_NAME_FETCHED,
    REMOTE_NAME_FAILURE,
};

namespace TYPE_OF_SERVICE


@@ 96,37 100,25 @@ enum class DeviceState
struct Devicei : public Device
{
  public:
    bd_addr_t address;
    uint8_t pageScanRepetitionMode;
    uint16_t clockOffset;
    uint32_t classOfDevice; ///> Class of Device/Service
    mutable bd_addr_t address{};
    uint8_t pageScanRepetitionMode{};
    uint16_t clockOffset{};
    uint32_t classOfDevice{}; ///> Class of Device/Service
    DEVICE_STATE state;
    DeviceState deviceState;

    explicit Devicei(std::string name = "") : Device(std::move(name))
    {
        memset(address, 0, sizeof(address));
    }
    bool isPairingSSP = false;

    explicit Devicei(std::string name = "");
    explicit Devicei(bd_addr_t &address);
    Devicei &operator=(const Devicei &d);
    Devicei(const Devicei &d);
    Devicei &operator=(Devicei &&d) noexcept;
    Devicei(Devicei &&d) noexcept;
    ~Devicei() override = default;
    void setAddress(bd_addr_t *addr)
    {
        memcpy(&address, addr, sizeof address);
    }

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

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

    auto address_str() const -> const char *
    {
        return bd_addr_to_str(address);
    }
    void setAddress(bd_addr_t *addr);
    bool operator==(const Devicei &cmpDevice) const;
    bool operator!=(const Devicei &cmpDevice) const;
    auto address_str() const -> const char *;
};

struct DeviceMetadata_t

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

#include "error_bluetooth.hpp"
#include <module-bluetooth/lib/btstack/src/bluetooth.h>

namespace bluetooth
{
    const char *error_cstr(int err)
    {
        switch (err) {
        case ERROR_CODE_SUCCESS:
            return "ERROR_CODE_SUCCESS";
        case ERROR_CODE_UNKNOWN_HCI_COMMAND:
            return "ERROR_CODE_UNKNOWN_HCI_COMMAND";
        case ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER:
            return "ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER";
        case ERROR_CODE_HARDWARE_FAILURE:
            return "ERROR_CODE_HARDWARE_FAILURE";
        case ERROR_CODE_PAGE_TIMEOUT:
            return "ERROR_CODE_PAGE_TIMEOUT";
        case ERROR_CODE_AUTHENTICATION_FAILURE:
            return "ERROR_CODE_AUTHENTICATION_FAILURE";
        case ERROR_CODE_PIN_OR_KEY_MISSING:
            return "ERROR_CODE_PIN_OR_KEY_MISSING";
        case ERROR_CODE_MEMORY_CAPACITY_EXCEEDED:
            return "ERROR_CODE_MEMORY_CAPACITY_EXCEEDED";
        case ERROR_CODE_CONNECTION_TIMEOUT:
            return "ERROR_CODE_CONNECTION_TIMEOUT";
        case ERROR_CODE_CONNECTION_LIMIT_EXCEEDED:
            return "ERROR_CODE_CONNECTION_LIMIT_EXCEEDED";
        case ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED:
            return "ERROR_CODE_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED";
        case ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS:
            return "ERROR_CODE_ACL_CONNECTION_ALREADY_EXISTS";
        case ERROR_CODE_COMMAND_DISALLOWED:
            return "ERROR_CODE_COMMAND_DISALLOWED";
        case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES:
            return "ERROR_CODE_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES";
        case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS:
            return "ERROR_CODE_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS";
        case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR:
            return "ERROR_CODE_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR";
        case ERROR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED:
            return "ERROR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED";
        case ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE:
            return "ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE";
        case ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS:
            return "ERROR_CODE_INVALID_HCI_COMMAND_PARAMETERS";
        case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
            return "ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION";
        case ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES:
            return "ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES";
        case ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF:
            return "ERROR_CODE_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF";
        case ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST:
            return "ERROR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST";
        case ERROR_CODE_REPEATED_ATTEMPTS:
            return "ERROR_CODE_REPEATED_ATTEMPTS";
        case ERROR_CODE_PAIRING_NOT_ALLOWED:
            return "ERROR_CODE_PAIRING_NOT_ALLOWED";
        case ERROR_CODE_UNKNOWN_LMP_PDU:
            return "ERROR_CODE_UNKNOWN_LMP_PDU";
        case ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE_UNSUPPORTED_LMP_FEATURE:
            return "ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE_UNSUPPORTED_LMP_FEATURE";
        case ERROR_CODE_SCO_OFFSET_REJECTED:
            return "ERROR_CODE_SCO_OFFSET_REJECTED";
        case ERROR_CODE_SCO_INTERVAL_REJECTED:
            return "ERROR_CODE_SCO_INTERVAL_REJECTED";
        case ERROR_CODE_SCO_AIR_MODE_REJECTED:
            return "ERROR_CODE_SCO_AIR_MODE_REJECTED";
        case ERROR_CODE_INVALID_LMP_PARAMETERS_INVALID_LL_PARAMETERS:
            return "ERROR_CODE_INVALID_LMP_PARAMETERS_INVALID_LL_PARAMETERS";
        case ERROR_CODE_UNSPECIFIED_ERROR:
            return "ERROR_CODE_UNSPECIFIED_ERROR";
        case ERROR_CODE_UNSUPPORTED_LMP_PARAMETER_VALUE_UNSUPPORTED_LL_PARAMETER_VALUE:
            return "ERROR_CODE_UNSUPPORTED_LMP_PARAMETER_VALUE_UNSUPPORTED_LL_PARAMETER_VALUE";
        case ERROR_CODE_ROLE_CHANGE_NOT_ALLOWED:
            return "ERROR_CODE_ROLE_CHANGE_NOT_ALLOWED";
        case ERROR_CODE_LMP_RESPONSE_TIMEOUT_LL_RESPONSE_TIMEOUT:
            return "ERROR_CODE_LMP_RESPONSE_TIMEOUT_LL_RESPONSE_TIMEOUT";
        case ERROR_CODE_LMP_ERROR_TRANSACTION_COLLISION:
            return "ERROR_CODE_LMP_ERROR_TRANSACTION_COLLISION";
        case ERROR_CODE_LMP_PDU_NOT_ALLOWED:
            return "ERROR_CODE_LMP_PDU_NOT_ALLOWED";
        case ERROR_CODE_ENCRYPTION_MODE_NOT_ACCEPTABLE:
            return "ERROR_CODE_ENCRYPTION_MODE_NOT_ACCEPTABLE";
        case ERROR_CODE_LINK_KEY_CANNOT_BE_CHANGED:
            return "ERROR_CODE_LINK_KEY_CANNOT_BE_CHANGED";
        case ERROR_CODE_REQUESTED_QOS_NOT_SUPPORTED:
            return "ERROR_CODE_REQUESTED_QOS_NOT_SUPPORTED";
        case ERROR_CODE_INSTANT_PASSED:
            return "ERROR_CODE_INSTANT_PASSED";
        case ERROR_CODE_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED:
            return "ERROR_CODE_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED";
        case ERROR_CODE_DIFFERENT_TRANSACTION_COLLISION:
            return "ERROR_CODE_DIFFERENT_TRANSACTION_COLLISION";
        case ERROR_CODE_RESERVED:
            return "ERROR_CODE_RESERVED";
        case ERROR_CODE_QOS_UNACCEPTABLE_PARAMETER:
            return "ERROR_CODE_QOS_UNACCEPTABLE_PARAMETER";
        case ERROR_CODE_QOS_REJECTED:
            return "ERROR_CODE_QOS_REJECTED";
        case ERROR_CODE_CHANNEL_CLASSIFICATION_NOT_SUPPORTED:
            return "ERROR_CODE_CHANNEL_CLASSIFICATION_NOT_SUPPORTED";
        case ERROR_CODE_INSUFFICIENT_SECURITY:
            return "ERROR_CODE_INSUFFICIENT_SECURITY";
        case ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE:
            return "ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE";
        case ERROR_CODE_ROLE_SWITCH_PENDING:
            return "ERROR_CODE_ROLE_SWITCH_PENDING";
        case ERROR_CODE_RESERVED_SLOT_VIOLATION:
            return "ERROR_CODE_RESERVED_SLOT_VIOLATION";
        case ERROR_CODE_ROLE_SWITCH_FAILED:
            return "ERROR_CODE_ROLE_SWITCH_FAILED";
        case ERROR_CODE_EXTENDED_INQUIRY_RESPONSE_TOO_LARGE:
            return "ERROR_CODE_EXTENDED_INQUIRY_RESPONSE_TOO_LARGE";
        case ERROR_CODE_SECURE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST:
            return "ERROR_CODE_SECURE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST";
        case ERROR_CODE_HOST_BUSY_PAIRING:
            return "ERROR_CODE_HOST_BUSY_PAIRING";
        case ERROR_CODE_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND:
            return "ERROR_CODE_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND";
        case ERROR_CODE_CONTROLLER_BUSY:
            return "ERROR_CODE_CONTROLLER_BUSY";
        case ERROR_CODE_UNACCEPTABLE_CONNECTION_PARAMETERS:
            return "ERROR_CODE_UNACCEPTABLE_CONNECTION_PARAMETERS";
        case ERROR_CODE_DIRECTED_ADVERTISING_TIMEOUT:
            return "ERROR_CODE_DIRECTED_ADVERTISING_TIMEOUT";
        case ERROR_CODE_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE:
            return "ERROR_CODE_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE";
        case ERROR_CODE_CONNECTION_FAILED_TO_BE_ESTABLISHED:
            return "ERROR_CODE_CONNECTION_FAILED_TO_BE_ESTABLISHED";
        case ERROR_CODE_MAC_CONNECTION_FAILED:
            return "ERROR_CODE_MAC_CONNECTION_FAILED";
        case ERROR_CODE_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING:
            return "ERROR_CODE_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING";
        default:
            return "ERROR_CODE_UNDEFINED";
        }
    }
} // namespace bluetooth

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

/// all these are taken from the defines to have textual representation
namespace bluetooth
{
    const char *error_cstr(int err);
}

M module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp => module-bluetooth/Bluetooth/interface/BluetoothDriverImpl.cpp +3 -0
@@ 82,6 82,9 @@ namespace bluetooth
        hci_set_link_key_db(bluetooth::KeyStorage::getKeyStorage());
        hci_event_callback_registration.callback = &hci_packet_handler;
        hci_add_event_handler(&hci_event_callback_registration);

        gap_ssp_set_io_capability(SSP_IO_CAPABILITY_KEYBOARD_ONLY);
        gap_ssp_set_auto_accept(false);
        LOG_DEBUG("BT worker run success");
        return Error::Success;
    }

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

#include "Devices.hpp"
#include <btstack_util.h>

namespace bluetooth::gap
{
    auto Devices::getList() -> std::vector<Devicei>
    {
        return {devices.begin(), devices.end()};
    }

    Devices::iter Devices::find(bd_addr_t &addr)
    {
        return std::find_if(std::begin(devices), std::end(devices), [addr](const Devicei &device) {
            return bd_addr_cmp(addr, device.address) == 0;
        });
    }

    Devices::iter Devices::find(DEVICE_STATE st)
    {
        return std::find_if(
            std::begin(devices), std::end(devices), [st](const Devicei &device) { return st == device.state; });
    }

    void Devices::for_each(const std::function<void(Devicei &)> &f)
    {
        if (not f) {
            return;
        }
        for (auto &el : devices) {
            f(el);
        }
    }

    Devices::iter Devices::end()
    {
        return std::end(devices);
    }

    Devices::iter Devices::put(Devicei &&dev)
    {
        if (const auto &it = find(dev.address); it != end()) {
            devices.erase(it);
        }
        devices.emplace_back(dev);
        return std::prev(devices.end());
    }

    Devices::iter Devices::put(bd_addr_t &addr)
    {
        if (const auto &it = find(addr); it != end()) {
            devices.erase(it);
        }
        devices.emplace_back(Devicei(addr));
        return std::prev(devices.end());
    }

    void Devices::clear()
    {
        devices.clear();
    }
} // namespace bluetooth::gap

A module-bluetooth/Bluetooth/interface/profiles/GAP/Devices.hpp => module-bluetooth/Bluetooth/interface/profiles/GAP/Devices.hpp +30 -0
@@ 0,0 1,30 @@
// 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 <Bluetooth/Device.hpp>
#include <list>
#include <vector>
#include <functional>

namespace bluetooth::gap
{
    /// class to store and get devices in GAP operations
    class Devices
    {
        std::list<Devicei> devices;

      public:
        using iter = decltype(devices)::iterator;
        iter find(bd_addr_t &addr);
        iter find(DEVICE_STATE st);
        void for_each(const std::function<void(Devicei &)> &f);
        iter end();
        // adds element, if element already on list - remove and add anew
        iter put(Devicei &&dev);
        // same as above
        iter put(bd_addr_t &addr);
        void clear();
        auto getList() -> std::vector<Devicei>;
    };
} // namespace bluetooth::gap

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.cpp +190 -89
@@ 2,12 2,17 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "GAP.hpp"
#include "Devices.hpp"
#include "GAP/used_events.hpp"

#include <log/log.hpp>
#include <Bluetooth/error_bluetooth.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-bluetooth/messages/Unpair.hpp>
#include <service-bluetooth/messages/Passkey.hpp>
#include <service-bluetooth/Constants.hpp>
#include <log/log.hpp>
#include <memory>
extern "C"
{
#include "btstack.h"


@@ 15,11 20,27 @@ extern "C"
};
namespace bluetooth
{
    Devicei GAP::currentlyProccesedDevice;
    sys::Service *GAP::ownerService = nullptr;
    std::vector<Devicei> GAP::devices;
    btstack_packet_callback_registration_t GAP::cb_handler;
    ScanState GAP::state;
    stack::state GAP::state;

    namespace gap
    {
        enum class state
        {
            scan_off = 0,
            scan_on,
        } static state;
    }

    static gap::Devices &devices()
    {
        static std::unique_ptr<gap::Devices> dev;
        if (not dev) {
            dev = std::make_unique<gap::Devices>();
        }
        return *dev;
    };

    auto GAP::registerScan() -> Error
    {


@@ 34,11 55,15 @@ namespace bluetooth
    auto GAP::scan() -> Error
    {
        if (hci_get_state() == HCI_STATE_WORKING) {
            devices.clear();
            if (gap::state == gap::state::scan_on) {
                stopScan();
            }
            devices().clear();
            if (auto ret = startScan(); ret != 0) {
                LOG_ERROR("Start scan error!: 0x%X", ret);
                LOG_ERROR("Start scan error!: 0x%02X - %s", ret, error_cstr(ret));
                return Error(Error::LibraryError, ret);
            }
            gap::state = gap::state::scan_on;
        }
        else {
            return Error(Error::NotReady);


@@ 48,6 73,7 @@ namespace bluetooth

    void GAP::stopScan()
    {
        gap::state = gap::state::scan_off;
        gap_inquiry_force_stop();
        LOG_INFO("Scan stopped!");
    }


@@ 61,30 87,20 @@ namespace bluetooth
    auto GAP::pair(Devicei device, std::uint8_t protectionLevel) -> bool
    {
        if (hci_get_state() == HCI_STATE_WORKING) {
            auto devIndex            = getDeviceIndexForAddress(devices, device.address);
            currentlyProccesedDevice = devices.at(devIndex);

            auto it = devices().find(device.address);
            if (it == devices().end()) {
                LOG_ERROR("device not found: %s", device.address_str());
                return false;
            }
            return gap_dedicated_bonding(device.address, protectionLevel) == 0;
        }
        return false;
    }

    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;
        });

        if (result == std::end(devs)) {
            return -1;
        }

        return std::distance(std::begin(devs), result);
    }

    void GAP::sendDevices()
    {
        auto msg = std::make_shared<message::bluetooth::ResponseVisibleDevices>(devices);
        auto msg = std::make_shared<message::bluetooth::ResponseVisibleDevices>(devices().getList());
        ownerService->bus.sendMulticast(std::move(msg), sys::BusChannel::BluetoothNotifications);
    }



@@ 94,48 110,28 @@ namespace bluetooth
        return gap_inquiry_start(inquiryIntervalSeconds);
    }

    auto GAP::remoteNameToFetch() -> bool
    {
        auto result = std::find_if(std::begin(devices), std::end(devices), [](Devicei &device) {
            return device.state == REMOTE_NAME_REQUEST;
        });

        return result != std::end(devices);
    }

    void GAP::fetchRemoteName()
    {
        for (auto &device : bluetooth::GAP::devices) {
            if (device.state == REMOTE_NAME_REQUEST) {
                device.state = REMOTE_NAME_INQUIRED;
                LOG_INFO("Get remote name...");
                gap_remote_name_request(device.address, device.pageScanRepetitionMode, device.clockOffset | 0x8000);
                return;
            }
        }
    }

    void GAP::continueScanning()
    {
        if (remoteNameToFetch()) {
            fetchRemoteName();
        if (const auto &it = devices().find(REMOTE_NAME_REQUEST); it != devices().end()) {
            LOG_INFO("Get remote name for %s", it->name.data());
            it->state = REMOTE_NAME_INQUIRED;
            gap_remote_name_request(it->address, it->pageScanRepetitionMode, it->clockOffset | 0x8000);
            return;
        }
        startScan();
        if (gap::state == gap::state::scan_on) {
            startScan();
        }
    }

    auto GAP::updateDeviceName(std::uint8_t *packet, bd_addr_t &addr) -> bool
    {
        reverse_bd_addr(&packet[3], addr);
        auto index = getDeviceIndexForAddress(devices, addr);
        if (index >= 0) {
            if (packet[2] == 0) {
                devices[index].state = REMOTE_NAME_FETCHED;
                strcpy(devices[index].name.data(), reinterpret_cast<const char *>(&packet[9]));
                return true;
            }
            else {
                LOG_INFO("Failed to get name: page timeout");
        if (auto it = devices().find(addr); it != devices().end()) {
            it->state = packet[2] ? REMOTE_NAME_FAILURE : REMOTE_NAME_FETCHED;
            if (it->state != REMOTE_NAME_FAILURE) {
                strcpy(it->name.data(), reinterpret_cast<const char *>(&packet[9]));
            }
            return it->state == REMOTE_NAME_FETCHED;
        }
        return false;
    }


@@ 148,6 144,7 @@ namespace bluetooth
        device.clockOffset            = gap_event_inquiry_result_get_clock_offset(packet);
        device.classOfDevice          = gap_event_inquiry_result_get_class_of_device(packet);
        LOG_INFO("Device found ");
        LOG_INFO("with address: %s, ", device.address_str());
        LOG_INFO("with COD: 0x%06x, ", static_cast<unsigned int>(device.classOfDevice));
        LOG_INFO("pageScan %d, ", device.pageScanRepetitionMode);
        LOG_INFO("clock offset 0x%04x", device.clockOffset);


@@ 171,14 168,15 @@ namespace bluetooth
            strcpy(device.name.data(), bd_addr_to_str(devAddr));
        }

        devices.emplace_back(std::move(device));
        devices().put(std::move(device));
    }

    void GAP::processInquiryResult(std::uint8_t *packet, bd_addr_t &addr)
    void GAP::processInquiryResult(std::uint8_t *packet)
    {
        bd_addr_t addr;
        gap_event_inquiry_result_get_bd_addr(packet, addr);
        auto index = getDeviceIndexForAddress(devices, addr);
        if (index >= 0) {
        auto it = devices().find(addr);
        if (it != devices().end()) {
            return; // already in our list
        }
        uint32_t classOfDevice = gap_event_inquiry_result_get_class_of_device(packet);


@@ 194,12 192,11 @@ namespace bluetooth
    }
    void GAP::processInquiryComplete()
    {
        for (auto &device : devices) {
            // retry remote name request
            if (device.state == REMOTE_NAME_INQUIRED) {
                device.state = REMOTE_NAME_REQUEST;
        devices().for_each([](Devicei &d) {
            if (d.state == REMOTE_NAME_INQUIRED) {
                d.state = REMOTE_NAME_REQUEST;
            }
        }
        });
        continueScanning();
    }
    void GAP::processNameRequestComplete(std::uint8_t *packet, bd_addr_t &addr)


@@ 209,13 206,15 @@ namespace bluetooth
        }
        continueScanning();
    }

    void GAP::processDedicatedBondingCompleted(std::uint8_t *packet, bd_addr_t &addr)
    {
        auto result = packet[2];

        auto msg = std::make_shared<BluetoothPairResultMessage>(currentlyProccesedDevice, result == 0u);
        ownerService->bus.sendUnicast(std::move(msg), "ServiceBluetooth");
        auto it     = devices().find(addr);
        auto msg = std::make_shared<BluetoothPairResultMessage>(it != devices().end() ? *it : Devicei(), result == 0u);
        ownerService->bus.sendUnicast(std::move(msg), service::name::bluetooth);
    }

    /* @text In ACTIVE, the following events are processed:
     *  - GAP Inquiry result event: BTstack provides a unified inquiry result that contain
     *    Class of Device (CoD), page scan mode, clock offset. RSSI and name (from EIR) are optional.


@@ 225,71 224,136 @@ namespace bluetooth
     *  - Remote name request complete event: the remote name is stored in the table and the
     *    state is updated to REMOTE_NAME_FETCHED. The query of remote names is continued.
     */
    void GAP::activeStateHandler(std::uint8_t eventType, std::uint8_t *packet, bd_addr_t &addr)
    void GAP::activeStateHandler(std::uint8_t eventType, std::uint8_t *packet, std::uint16_t size)
    {
        if (not(eventType == HCI_EVENT_TRANSPORT_PACKET_SENT || eventType == HCI_EVENT_COMMAND_STATUS ||
                eventType == HCI_EVENT_INQUIRY_COMPLETE || eventType == HCI_EVENT_COMMAND_COMPLETE)) {
            LOG_DEBUG("event: 0x%02X - %s - size: %" PRIu16, eventType, evt_cstr(eventType), size);
        }
        switch (eventType) {
        case HCI_EVENT_TRANSPORT_PACKET_SENT:
            break;
        case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE:
            break;
        case GAP_EVENT_PAIRING_STARTED:
            break;

        case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
            break;

        case HCI_EVENT_USER_CONFIRMATION_REQUEST: {
            bd_addr_t addr;
            hci_event_user_confirmation_request_get_bd_addr(packet, addr);
            hci_connection_t *conn = hci_connection_for_bd_addr_and_type(addr, BD_ADDR_TYPE_ACL);
            hci_send_cmd(&hci_user_confirmation_request_reply, &conn->address);
        } break;

        case HCI_EVENT_PIN_CODE_REQUEST: {
            bd_addr_t addr;
            hci_event_pin_code_request_get_bd_addr(packet, addr);
            auto it = devices().find(addr);
            if (it == devices().end()) {
                gap_remote_name_request(addr, PAGE_SCAN_MODE_STANDARD, 0);
                it = devices().put(addr);
            }
            it->isPairingSSP = false;

            auto msg = std::make_shared<::message::bluetooth::RequestPasskey>(*it);
            ownerService->bus.sendMulticast(std::move(msg), sys::BusChannel::BluetoothNotifications);
        } break;

        case HCI_EVENT_READ_REMOTE_EXTENDED_FEATURES_COMPLETE: {
            uint16_t handle        = little_endian_read_16(packet, 3);
            hci_connection_t *conn = hci_connection_for_handle(handle);
            auto yes               = gap_ssp_supported_on_both_sides(conn->con_handle);
            auto it                = devices().find(conn->address);
            if (it == devices().end()) {
                return;
            }
            it->isPairingSSP = yes;
        } break;

        case GAP_EVENT_INQUIRY_RESULT:
            processInquiryResult(packet, addr);
            processInquiryResult(packet);
            break;

        case GAP_EVENT_INQUIRY_COMPLETE:
            processInquiryComplete();
            break;

        case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
        case HCI_EVENT_USER_PASSKEY_REQUEST: {
            bd_addr_t addr;
            hci_event_user_passkey_request_get_bd_addr(packet, addr);
            auto it = devices().find(addr);
            if (it == devices().end()) {
                gap_remote_name_request(addr, PAGE_SCAN_MODE_STANDARD, 0);
                it = devices().put(addr);
            }
            it->isPairingSSP = true;
            ownerService->bus.sendMulticast(std::make_shared<::message::bluetooth::RequestPasskey>(*it),
                                            sys::BusChannel::BluetoothNotifications);
        } break;
        case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: {
            bd_addr_t addr;
            hci_event_remote_name_request_complete_get_bd_addr(packet, addr);
            processNameRequestComplete(packet, addr);
            break;
        } break;
        case GAP_EVENT_DEDICATED_BONDING_COMPLETED:
            bd_addr_t addr;
            reverse_bd_addr(&packet[3], addr);
            processDedicatedBondingCompleted(packet, addr);
            break;
        case HCI_EVENT_SIMPLE_PAIRING_COMPLETE: {
            bd_addr_t addr;
            hci_event_simple_pairing_complete_get_bd_addr(packet, addr);
            processSimplePairingCompleted(packet, addr);
        } break;
        case GAP_EVENT_PAIRING_COMPLETE:
            LOG_DEBUG("status: 0x%02X", packet[10]);
            break;
        default:
            break;
        }
    }

    void GAP::initStateHandler(std::uint8_t eventType, std::uint8_t *packet)
    {
        if (eventType == BTSTACK_EVENT_STATE) {
            if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
                state = ScanState::active;
                state = stack::state::working;
            }
        }
    }

    void GAP::packetHandler(std::uint8_t packet_type, std::uint16_t channel, std::uint8_t *packet, std::uint16_t size)
    {
        bd_addr_t addr;

        if (packet_type != HCI_EVENT_PACKET) {
            return;
        }
        if (hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST) {
            bd_addr_t address;
            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.sendMulticast(std::move(msg), sys::BusChannel::BluetoothNotifications);
        }
        const auto eventType = hci_event_packet_get_type(packet);
        switch (state) {
        case ScanState::init:
        case stack::state::init:
            initStateHandler(eventType, packet);
            break;
        case ScanState::active:
            activeStateHandler(eventType, packet, addr);
        case stack::state::working:
            activeStateHandler(eventType, packet, size);
            break;
        default:
            break;
        }
    }

    GAP::GAP(sys::Service *owner)
    {
        ownerService = owner;
        state        = ScanState::init;
        state        = stack::state::init;
    }
    auto GAP::getDevicesList() -> const std::vector<Devicei> &

    auto GAP::getDevicesList() -> std::vector<Devicei>
    {
        return devices;
        return devices().getList();
    }

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


@@ 300,8 364,45 @@ namespace bluetooth
                                        sys::BusChannel::BluetoothNotifications);
        return true;
    }
    void GAP::respondPinCode(const std::string &pin)

    void GAP::respondPinCode(const std::string &pin, Devicei d)
    {
        LOG_DEBUG("pairing response for device: %s pin: %s is SSP? %s",
                  d.address_str(),
                  pin.c_str(),
                  d.isPairingSSP ? "yes" : "no");
        if (!d.isPairingSSP) {
            gap_pin_code_response(d.address, pin.c_str());
            return;
        }

        unsigned int passkey = 0;
        try {
            passkey = stoi(pin);
            LOG_DEBUG("Sending %06u as a passkey", passkey);
        }
        catch (const std::invalid_argument &e) {
            LOG_ERROR("STOI error: %s", e.what());
        }

        gap_ssp_passkey_response(d.address, passkey);
    }

    void GAP::processSimplePairingCompleted(std::uint8_t *packet, bd_addr_t &addr)
    {
        gap_pin_code_response(currentlyProccesedDevice.address, pin.c_str());
        auto status = hci_event_simple_pairing_complete_get_status(packet);
        auto it     = devices().find(addr);
        LOG_INFO("HCI_EVENT_SIMPLE_PAIRING_COMPLETE: 0x%02X - %s - device found: %s : address:  %s",
                 status,
                 error_cstr(status),
                 it != devices().end() ? "found" : "fail",
                 bd_addr_to_str(addr));
        if (it == devices().end()) {
            auto msg = std::make_shared<BluetoothPairResultMessage>(Devicei(), false);
            ownerService->bus.sendUnicast(std::move(msg), service::name::bluetooth);
            return;
        }
        auto msg = std::make_shared<BluetoothPairResultMessage>(*it, status == ERROR_CODE_SUCCESS);
        ownerService->bus.sendUnicast(std::move(msg), service::name::bluetooth);
    }
} // namespace bluetooth

M module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp => module-bluetooth/Bluetooth/interface/profiles/GAP/GAP.hpp +18 -13
@@ 12,35 12,40 @@ extern "C"
}
namespace bluetooth
{
    enum ScanState
    namespace stack
    {
        init,
        active,
        done
    };
        enum state
        {
            off,
            init,
            working,
            halting,
            sleeping,
            falling_asleep
        };
    }

    class GAP
    {
        static std::vector<Devicei> devices;
        static sys::Service *ownerService;
        static btstack_packet_callback_registration_t cb_handler;
        static constexpr auto inquiryIntervalSeconds = 5;
        static ScanState state;
        static stack::state state;
        static void sendDevices();
        static auto startScan() -> int;
        static auto remoteNameToFetch() -> bool;
        static void fetchRemoteName();
        static void continueScanning();
        static auto updateDeviceName(std::uint8_t *packet, bd_addr_t &addr) -> bool;
        static void addNewDevice(std::uint8_t *packet, bd_addr_t &addr);
        static void activeStateHandler(std::uint8_t eventType, std::uint8_t *packet, bd_addr_t &addr);
        static void activeStateHandler(std::uint8_t eventType, std::uint8_t *packet, std::uint16_t size);
        static void packetHandler(std::uint8_t packet_type,
                                  std::uint16_t channel,
                                  std::uint8_t *packet,
                                  std::uint16_t size);
        static void processInquiryResult(std::uint8_t *packet, bd_addr_t &addr);
        static void processInquiryResult(std::uint8_t *packet);
        static void processInquiryComplete();
        static void processNameRequestComplete(std::uint8_t *packet, bd_addr_t &addr);
        static void processDedicatedBondingCompleted(std::uint8_t *packet, bd_addr_t &addr);
        static void processSimplePairingCompleted(std::uint8_t *packet, bd_addr_t &addr);
        static void initStateHandler(std::uint8_t eventType, std::uint8_t *packet);
        static auto getDeviceIndexForAddress(const std::vector<Devicei> &devs, const bd_addr_t addr) -> int;



@@ 52,8 57,8 @@ namespace bluetooth
        void setVisibility(bool visibility);
        auto pair(Devicei device, std::uint8_t protectionLevel = 0) -> bool;
        auto unpair(Devicei device) -> bool;
        static auto getDevicesList() -> const std::vector<Devicei> &;
        static void respondPinCode(const std::string &pin);
        static auto getDevicesList() -> std::vector<Devicei>;
        static void respondPinCode(const std::string &pin, Devicei d);

        static Devicei currentlyProccesedDevice;
        explicit GAP(sys::Service *owner);

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

#include "used_events.hpp"
#include <module-bluetooth/lib/btstack/src/btstack_event.h>

namespace bluetooth
{
    const char *evt_cstr(int evt)
    {
        switch (evt) {
        // ----------
        // HCI EVENTS
        // ----------
        case HCI_EVENT_TRANSPORT_PACKET_SENT:
            return "HCI_EVENT_TRANSPORT_PACKET_SENT";
        case HCI_EVENT_COMMAND_COMPLETE:
            return "HCI_EVENT_COMMAND_COMPLETE";
        case HCI_EVENT_COMMAND_STATUS:
            return "HCI_EVENT_COMMAND_STATUS";
        case HCI_EVENT_INQUIRY_COMPLETE:
            return "HCI_EVENT_INQUIRY_COMPLETE";
        case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE:
            return "HCI_EVENT_EXTENDED_INQUIRY_RESPONSE";
        case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
            return "HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE";
        case HCI_EVENT_USER_CONFIRMATION_REQUEST:
            return "HCI_EVENT_USER_CONFIRMATION_REQUEST";
        case HCI_EVENT_PIN_CODE_REQUEST:
            return "HCI_EVENT_PIN_CODE_REQUEST";
        case HCI_EVENT_READ_REMOTE_EXTENDED_FEATURES_COMPLETE:
            return "HCI_EVENT_READ_REMOTE_EXTENDED_FEATURES_COMPLETE";
        case HCI_EVENT_USER_PASSKEY_REQUEST:
            return "HCI_EVENT_USER_PASSKEY_REQUEST";
        case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
            return "HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE";
        case HCI_EVENT_SIMPLE_PAIRING_COMPLETE:
            return "HCI_EVENT_SIMPLE_PAIRING_COMPLETE";

        // ----------
        // GAP EVENTS
        // ----------
        case GAP_EVENT_PAIRING_COMPLETE:
            return "GAP_EVENT_PAIRING_COMPLETE";
        case GAP_EVENT_DEDICATED_BONDING_COMPLETED:
            return "GAP_EVENT_DEDICATED_BONDING_COMPLETED";
        case GAP_EVENT_INQUIRY_RESULT:
            return "GAP_EVENT_INQUIRY_RESULT";
        case GAP_EVENT_INQUIRY_COMPLETE:
            return "GAP_EVENT_INQUIRY_COMPLETE";
        case GAP_EVENT_PAIRING_STARTED:
            return "GAP_EVENT_PAIRING_STARTED";
        default:
            return "EVENT_UNKNOWN";
        }
    }
} // namespace bluetooth

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

#pragma once

namespace bluetooth
{
    const char *evt_cstr(int evt);
}

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +4 -0
@@ 10,12 10,16 @@ module_is_test_entity()
set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/audio/BluetoothAudioDevice.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BluetoothWorker.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/error_bluetooth.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/WorkerController.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/CommandHandler.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/Device.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/glucode/BluetoothRunLoop.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/BluetoothDriverImpl.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtKeysStorage.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/GAP/GAP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/GAP/Devices.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/GAP/used_events.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/A2DP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVRCP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVDTP.cpp

M module-bluetooth/tests/CMakeLists.txt => module-bluetooth/tests/CMakeLists.txt +1 -0
@@ 5,6 5,7 @@ add_catch2_executable(
        tests-main.cpp
        tests-StatefulController.cpp
        tests-BluetoothDevicesModel.cpp
        tests-Devicei.cpp
    LIBS
        module-sys
        module-bluetooth

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

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

    controller.processCommand(bluetooth::Command(Command::Type::PowerOn));
    REQUIRE(controller.isOn());
}

M module-bluetooth/tests/tests-BluetoothDevicesModel.cpp => module-bluetooth/tests/tests-BluetoothDevicesModel.cpp +1 -0
@@ 4,6 4,7 @@
#include <catch2/catch.hpp>
#include "Device.hpp"
#include "service-bluetooth/BluetoothDevicesModel.hpp"

TEST_CASE("Devicei comparison")
{
    Devicei device1{"Dev1"};

A module-bluetooth/tests/tests-Devicei.cpp => module-bluetooth/tests/tests-Devicei.cpp +62 -0
@@ 0,0 1,62 @@
// 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 <btstack_util.h>

Devicei genDev()
{
    Devicei from("from");
    bd_addr_t addr{0, 1, 2, 3, 4, 5};
    from.setAddress(&addr);
    from.pageScanRepetitionMode = 1;
    from.clockOffset            = 1;
    from.classOfDevice          = 1;
    from.state                  = DEVICE_STATE::REMOTE_NAME_REQUEST;
    from.deviceState            = DeviceState::Paired;
    from.isPairingSSP           = true;
    return from;
}

TEST_CASE("Devicei - create copy and move")
{
    Devicei from = genDev();
    Devicei to;

    SECTION("create - by copy")
    {
        SECTION("ctor")
        {
            to = Devicei(from);
        }
        SECTION("operator")
        {
            to.operator=(from);
        }
    }

    SECTION("move")
    {
        // please see that that section depends on previous section working fine
        Devicei base = from;
        SECTION("ctor")
        {
            to = std::move(base);
        }
        SECTION("operator")
        {
            to.operator=(std::move(from));
        }
    }

    REQUIRE(from == to);
    REQUIRE(!(from != to));
    REQUIRE(bd_addr_cmp(from.address, to.address) == 0);
    REQUIRE(from.pageScanRepetitionMode == to.pageScanRepetitionMode);
    REQUIRE(from.clockOffset == to.clockOffset);
    REQUIRE(from.classOfDevice == to.classOfDevice);
    REQUIRE(from.state == to.state);
    REQUIRE(from.deviceState == to.deviceState);
    REQUIRE(from.isPairingSSP == to.isPairingSSP);
}

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +1 -1
@@ 349,7 349,7 @@ auto ServiceBluetooth::handle(message::bluetooth::DisconnectResult *msg) -> std:
auto ServiceBluetooth::handle(message::bluetooth::ResponsePasskey *msg) -> std::shared_ptr<sys::Message>
{
    auto passKey = msg->getPasskey();
    bluetooth::GAP::respondPinCode(passKey);
    bluetooth::GAP::respondPinCode(passKey, msg->getDevice());
    return sys::MessageNone{};
}


M module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.cpp => module-services/service-bluetooth/service-bluetooth/BluetoothDevicesModel.cpp +6 -0
@@ 70,11 70,17 @@ void BluetoothDevicesModel::syncDevicesWithApp()
                                   sys::BusChannel::BluetoothNotifications);
    }
}

void BluetoothDevicesModel::setInternalDeviceState(const Devicei &device, const DeviceState &state)
{
    auto dev                      = getDeviceByAddress(device.address);
    if (not dev) {
        LOG_ERROR("no such device - ignored");
        return;
    }
    dev.value().get().deviceState = state;
}

void BluetoothDevicesModel::mergeInternalDeviceState(const Devicei &device)
{
    try {

D module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.cpp => module-services/service-bluetooth/service-bluetooth/BluetoothSettingsModel.cpp +0 -172
@@ 1,172 0,0 @@
// 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;
}

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

#pragma once

#include "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/messages/Passkey.hpp => module-services/service-bluetooth/service-bluetooth/messages/Passkey.hpp +21 -2
@@ 8,18 8,37 @@
namespace message::bluetooth
{
    class RequestPasskey : public BluetoothMessage
    {};
    {
        Devicei device;

      public:
        explicit RequestPasskey(const Devicei &dev) : device(dev)
        {}

        [[nodiscard]] auto getDevice()
        {
            return device;
        }
    };

    class ResponsePasskey : public BluetoothMessage
    {
        Devicei device;

      public:
        explicit ResponsePasskey(std::string passkey) : passkey(std::move(passkey))
        ResponsePasskey(std::string passkey, const Devicei &dev) : device(dev), passkey(std::move(passkey))
        {}

        [[nodiscard]] auto getPasskey() const -> std::string
        {
            return passkey;
        }

        [[nodiscard]] auto getDevice()
        {
            return device;
        }

      private:
        std::string passkey;
    };

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