~aleteoryx/muditaos

71a82520296605c6ce9be4473b38f58b9c6ed445 — SP2FET 5 years ago f75c6ad
[EGD-4580] Add BT settings middleware

Added bluetooth settings middleware to store BT status in settings.
Added middleware layer to handle new settings queries.
30 files changed, 481 insertions(+), 207 deletions(-)

M changelog.md
D image/user/db/settings_v2_001.sql
M module-apps/CMakeLists.txt
M module-apps/application-settings-new/ApplicationSettings.cpp
A module-apps/application-settings-new/data/BondedDevicesData.hpp
A module-apps/application-settings-new/data/PhoneNameData.hpp
M module-apps/application-settings-new/windows/AllDevicesWindow.cpp
M module-apps/application-settings-new/windows/AllDevicesWindow.hpp
M module-apps/application-settings-new/windows/BluetoothWindow.cpp
M module-apps/application-settings-new/windows/BluetoothWindow.hpp
M module-apps/application-settings-new/windows/PhoneNameWindow.cpp
M module-apps/application-settings-new/windows/PhoneNameWindow.hpp
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.hpp
M module-bluetooth/Bluetooth/BtCommand.hpp
M module-bluetooth/Bluetooth/BtKeysStorage.cpp
M module-bluetooth/Bluetooth/BtKeysStorage.hpp
M module-bluetooth/Bluetooth/BtstackWorker.cpp
M module-bluetooth/Bluetooth/interface/profiles/GAP.cpp
M module-services/service-bluetooth/CMakeLists.txt
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp
M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp
M module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp
M module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp
A module-services/service-bluetooth/service-bluetooth/SettingsSerializer.cpp
A module-services/service-bluetooth/service-bluetooth/SettingsSerializer.hpp
M module-services/service-db/agents/settings/SystemSettings.hpp
M module-services/service-desktop/endpoints/calllog/CalllogHelper.cpp
M module-utils/log/Logger.hpp
M changelog.md => changelog.md +1 -0
@@ 8,6 8,7 @@
* Add PLL2 clock switching
* Support for asynchronous callbacks on application side.
* APN settings window - empty.
* Bluetooth settings middleware layer

### Changed


D image/user/db/settings_v2_001.sql => image/user/db/settings_v2_001.sql +0 -43
@@ 1,43 0,0 @@
--
-- Main settings table, for string application persistent data
--
CREATE TABLE IF NOT EXISTS settings_tab (
    path TEXT NOT NULL UNIQUE PRIMARY KEY,
    value TEXT
);

--
-- Dictionary table, for variables with fixed set of values
--
CREATE TABLE IF NOT EXISTS dictionary_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT dictionary_unique
       UNIQUE (path, value) ON CONFLICT REPLACE
    );

--
-- Table contains information who to inform
-- about changes in values.
--
CREATE TABLE IF NOT EXISTS notifications_tab (
    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    service TEXT,
    CONSTRAINT notification_unique
        UNIQUE(path, service)
    );

CREATE TABLE IF NOT EXISTS settings_changed_tab(

    id INTEGER PRIMARY KEY,
    path TEXT NOT NULL,
    value TEXT,
    CONSTRAINT changed_unique
        UNIQUE(path ) ON CONFLICT REPLACE
);

CREATE TRIGGER IF NOT EXISTS on_update UPDATE OF value ON settings_tab 
WHEN new.value <> old.value BEGIN INSERT OR REPLACE INTO  settings_changed_tab (path, value) VALUES (new.path,new.value); END;


M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +1 -1
@@ 32,7 32,7 @@ set( SOURCES
    "widgets/BrightnessBox.cpp"
    "widgets/ModesBox.cpp"
    "widgets/BarGraph.cpp"
     )
)

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


M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +24 -1
@@ 33,7 33,11 @@
#include <service-cellular/CellularServiceAPI.hpp>
#include <service-db/Settings.hpp>
#include <module-services/service-bluetooth/service-bluetooth/messages/Status.hpp>
#include <service-bluetooth/messages/BondedDevices.hpp>
#include <service-bluetooth/messages/DeviceName.hpp>
#include <application-settings-new/data/BondedDevicesData.hpp>
#include <service-db/agents/settings/SystemSettings.hpp>
#include <application-settings-new/data/PhoneNameData.hpp>

namespace app
{


@@ 105,7 109,7 @@ namespace app
            }
        }

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

    // Invoked during initialization


@@ 118,6 122,25 @@ namespace app
            return ret;
        }

        connect(typeid(::message::bluetooth::ResponseDeviceName), [&](sys::Message *msg) {
            auto responseDeviceNameMsg = static_cast<::message::bluetooth::ResponseDeviceName *>(msg);
            if (gui::window::name::phone_name == getCurrentWindow()->getName()) {
                auto phoneNameData = std::make_unique<gui::PhoneNameData>(responseDeviceNameMsg->getName());
                switchWindow(gui::window::name::phone_name, std::move(phoneNameData));
            }
            return sys::MessageNone{};
        });

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

        createUserInterface();

        setActiveWindow(gui::name::window::main_window);

A module-apps/application-settings-new/data/BondedDevicesData.hpp => module-apps/application-settings-new/data/BondedDevicesData.hpp +24 -0
@@ 0,0 1,24 @@
// 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 <Device.hpp>

namespace gui
{

    class BondedDevicesData : public SwitchData
    {
      public:
        explicit BondedDevicesData(std::vector<Devicei> devices) : devices(std::move(devices))
        {}
        [[nodiscard]] auto getDevices() const noexcept -> const std::vector<Devicei> &
        {
            return devices;
        }

      private:
        const std::vector<Devicei> devices;
    };
} // namespace gui

A module-apps/application-settings-new/data/PhoneNameData.hpp => module-apps/application-settings-new/data/PhoneNameData.hpp +22 -0
@@ 0,0 1,22 @@
// 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 <string>
#include <SwitchData.hpp>
namespace gui
{
    class PhoneNameData : public SwitchData
    {
      public:
        explicit PhoneNameData(std::string name) : name(std::move(name))
        {}
        [[nodiscard]] auto getName() const -> const std::string &
        {
            return name;
        }

      private:
        const std::string name;
    };
} // namespace gui

M module-apps/application-settings-new/windows/AllDevicesWindow.cpp => module-apps/application-settings-new/windows/AllDevicesWindow.cpp +15 -10
@@ 8,18 8,29 @@
#include "OptionSetting.hpp"
#include "DialogMetadata.hpp"
#include "DialogMetadataMessage.hpp"
#include <Constants.hpp>

#include <InputEvent.hpp>
#include <i18n/i18n.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>

#include <service-bluetooth/messages/BondedDevices.hpp>
#include "application-settings-new/data/BondedDevicesData.hpp"
namespace gui
{

    AllDevicesWindow::AllDevicesWindow(app::Application *app) : OptionWindow(app, gui::window::name::all_devices)
    {
        setTitle(utils::localize.get("app_settings_bluetooth_all_devices"));
        addOptions(allDevicesOptionsList());
        sys::Bus::SendUnicast(
            std::make_shared<::message::bluetooth::RequestBondedDevices>(), service::name::bluetooth, application);
    }

    void AllDevicesWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        clearOptions();
        if (const auto newData = dynamic_cast<BondedDevicesData *>(data); newData != nullptr) {
            addOptions(allDevicesOptionsList(newData->getDevices()));
        }
    }

    auto AllDevicesWindow::onInput(const InputEvent &inputEvent) -> bool


@@ 44,17 55,11 @@ namespace gui
        return AppWindow::onInput(inputEvent);
    }

    auto AllDevicesWindow::allDevicesOptionsList() -> std::list<Option>
    auto AllDevicesWindow::allDevicesOptionsList(const std::vector<Devicei> &devicesList) -> std::list<Option>
    {
        std::list<gui::Option> optionsList;

        std::vector<Devicei> devices{Devicei("Paired_device1"),
                                     Devicei("Paired_device2"),
                                     Devicei("Paired_device3"),
                                     Devicei("Paired_device4"),
                                     Devicei("Paired_device5")};

        for (const auto &device : devices) {
        for (const auto &device : devicesList) {
            optionsList.emplace_back(std::make_unique<gui::OptionSettings>(
                device.name,
                [=](gui::Item &item) {

M module-apps/application-settings-new/windows/AllDevicesWindow.hpp => module-apps/application-settings-new/windows/AllDevicesWindow.hpp +7 -6
@@ 1,21 1,22 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "OptionWindow.hpp"

#include <Device.hpp>
namespace gui
{
    class AllDevicesWindow : public OptionWindow
    {
      public:
        AllDevicesWindow(app::Application *app);
        auto onInput(const InputEvent &inputEvent) -> bool override;
        explicit AllDevicesWindow(app::Application *app);

      private:
        auto allDevicesOptionsList(const std::vector<Devicei> &vector) -> std::list<Option>;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto onInput(const InputEvent &inputEvent) -> bool override;
        Image *leftArrowImage = nullptr;
        Image *crossImage     = nullptr;
        auto allDevicesOptionsList() -> std::list<Option>;
    };
}; // namespace gui

} // namespace gui

M module-apps/application-settings-new/windows/BluetoothWindow.cpp => module-apps/application-settings-new/windows/BluetoothWindow.cpp +2 -2
@@ 110,10 110,10 @@ namespace gui
        BluetoothStatus btStatus;

        if (isBluetoothSwitchOn) {
            btStatus.state = BluetoothStatus::BluetoothState::On;
            btStatus.state = BluetoothStatus::State::On;
        }
        else {
            btStatus.state = BluetoothStatus::BluetoothState::Off;
            btStatus.state = BluetoothStatus::State::Off;
        }
        btStatus.visibility = isPhoneVisibilitySwitchOn;
        ::message::bluetooth::SetStatus setStatus(btStatus);

M module-apps/application-settings-new/windows/BluetoothWindow.hpp => module-apps/application-settings-new/windows/BluetoothWindow.hpp +1 -1
@@ 29,7 29,7 @@ namespace gui
        {}
        [[nodiscard]] auto getState() const noexcept -> bool
        {
            if (status.state == BluetoothStatus::BluetoothState::On) {
            if (status.state == BluetoothStatus::State::On) {
                return true;
            }
            return false;

M module-apps/application-settings-new/windows/PhoneNameWindow.cpp => module-apps/application-settings-new/windows/PhoneNameWindow.cpp +30 -1
@@ 5,7 5,12 @@
#include "application-settings-new/ApplicationSettings.hpp"
#include "widgets/InputBox.hpp"

#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/messages/DeviceName.hpp>
#include <service-bluetooth/messages/SetDeviceName.hpp>

#include <Utils.hpp>
#include <application-settings-new/data/PhoneNameData.hpp>

namespace gui
{


@@ 30,12 35,36 @@ namespace gui
        bottomBar->setText(BottomBar::Side::CENTER, utils::localize.get(style::strings::common::save));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::localize.get(style::strings::common::back));

        sys::Bus::SendUnicast(
            std::make_shared<::message::bluetooth::RequestDeviceName>(), service::name::bluetooth, application);

        setFocusItem(inputField);
    }

    void PhoneNameWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        inputField->clear();
        setFocusItem(inputField);
        if (const auto newData = dynamic_cast<PhoneNameData *>(data); data != nullptr) {
            inputField->setText(newData->getName());
        }
    }

    auto PhoneNameWindow::onInput(const InputEvent &inputEvent) -> bool
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (!inputEvent.isShortPress()) {
            return false;
        }

        if (inputEvent.is(gui::KeyCode::KEY_ENTER) && !inputField->isEmpty()) {
            auto result = std::make_shared<::message::bluetooth::SetDeviceName>(inputField->getText());
            sys::Bus::SendUnicast(std::move(result), service::name::bluetooth, application);
            return true;
        }

        return false;
    }
} // namespace gui

M module-apps/application-settings-new/windows/PhoneNameWindow.hpp => module-apps/application-settings-new/windows/PhoneNameWindow.hpp +4 -6
@@ 1,10 1,7 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AppWindow.hpp"

#include <Text.hpp>

namespace gui


@@ 12,11 9,12 @@ namespace gui
    class PhoneNameWindow : public AppWindow
    {
      public:
        PhoneNameWindow(app::Application *app);
        void buildInterface() override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        explicit PhoneNameWindow(app::Application *app);

      private:
        void buildInterface() override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto onInput(const InputEvent &inputEvent) -> bool override;
        Text *inputField = nullptr;
    };


M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +78 -14
@@ 6,11 6,13 @@
#include "log/log.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
#include "BtKeysStorage.hpp"
extern "C"
{
#include "module-bluetooth/lib/btstack/src/btstack_util.h"
}
#include <btstack_run_loop_freertos.h>
#include <service-bluetooth/ServiceBluetooth.hpp>

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


@@ 41,15 43,23 @@ const char *c_str(Bt::Error::Code code)
    }
    return "";
}
namespace queues
{
    constexpr inline auto io  = "qBtIO";
    constexpr inline auto cmd = "qBtCmds";

} // namespace queues

BluetoothWorker::BluetoothWorker(sys::Service *service)
    : Worker(service), service(service), currentProfile(std::make_shared<Bt::HSP>())
    : Worker(service), service(service), currentProfile(std::make_shared<Bt::HSP>()),
      settings(static_cast<ServiceBluetooth *>(service)->settingsHolder)
{
    init({
        {"qBtIO", sizeof(Bt::Message), 10},
        {"qBtWork", sizeof(Bt::EvtWorker), 10},
        {queues::io, sizeof(Bt::Message), 10},
        {queues::cmd, sizeof(Bt::Command), 10},
    });
};
    static_cast<ServiceBluetooth *>(service)->workerQueue = Worker::getQueueHandleByName(queues::cmd);
}

BluetoothWorker::~BluetoothWorker()
{


@@ 69,14 79,33 @@ bool BluetoothWorker::run()
        is_running                          = true;
        auto el                             = queues[queueIO_handle];
        BlueKitchen::getInstance()->qHandle = el->GetQueueHandle();
        Bt::KeyStorage::settings            = settings;
        Bt::initialize_stack();
        Bt::register_hw_error_callback();
        Bt::GAP::register_scan();

        std::string name = "PurePhone";
        Bt::set_name(name);
        // set local namne
        Bt::GAP::set_visibility(true);
        // Bt::GAP::
        auto settingsName = std::get<std::string>(settings->getValue(Bluetooth::Settings::DeviceName));
        if (settingsName.empty()) {
            LOG_ERROR("settings name empty!");
            settings->setValue(Bluetooth::Settings::DeviceName, name);
            settingsName = name;
        }
        Bt::set_name(settingsName);
        Bt::GAP::set_visibility(
            std::visit(Bluetooth::BoolVisitor(), settings->getValue(Bluetooth::Settings::Visibility)));

        settings->setValue(Bluetooth::Settings::State, static_cast<int>(BluetoothStatus::State::On));
        settings->onLinkKeyAdded = [this](std::string addr) {
            for (auto &device : Bt::GAP::devices) {
                if (bd_addr_to_str(device.address) == addr) {
                    // found paired device
                    pairedDevices.emplace_back(device);
                    settings->setValue(Bluetooth::Settings::BondedDevices, SettingsSerializer::toString(pairedDevices));
                }
            }
        };

        Bt::run_stack(&this->bt_worker_task);
        return true;
    }


@@ 106,13 135,10 @@ void BluetoothWorker::stopScan()
    Bt::GAP::stop_scan();
}

bool BluetoothWorker::toggleVisibility()
void BluetoothWorker::setVisibility(bool visibility)
{
    static bool visibility = true;
    Bt::GAP::set_visibility(visibility);
    visibility = !visibility;

    return visibility;
    settings->setValue(Bluetooth::Settings::Visibility, visibility);
}

bool BluetoothWorker::start_pan()


@@ 124,15 150,53 @@ bool BluetoothWorker::start_pan()
    }
    return false;
}
bool BluetoothWorker::handleCommand(QueueHandle_t queue)
{
    Bt::Command command;
    if (xQueueReceive(queue, &command, 0) != pdTRUE) {
        LOG_ERROR("Queue receive failure!");
        return false;
    }
    switch (command) {
    case Bt::PowerOn:

        break;
    case Bt::StartScan:
        scan();
        break;
    case Bt::StopScan:
        stopScan();
        break;
    case Bt::VisibilityOn:
        setVisibility(true);
        break;
    case Bt::VisibilityOff:
        setVisibility(false);
        break;
    case Bt::ConnectAudio:
        establishAudioConnection();
        break;
    case Bt::DisconnectAudio:
        disconnectAudioConnection();
        break;
    case Bt::PowerOff:
        break;
    }
    return true;
}
bool BluetoothWorker::handleMessage(uint32_t queueID)
{

    QueueHandle_t queue = queues[queueID]->GetQueueHandle();
    if (queueID == queueService) {
        LOG_DEBUG("not interested");
        return true;
    }

    if (queueID == queueCommands) {
        handleCommand(queue);
        return true;
    }

    if (queueID != queueIO_handle) {
        LOG_ERROR("Wrong queue! %" PRIu32, queueID);
        return false;

M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +22 -4
@@ 11,6 11,7 @@
#include <memory>
#include <task.h>
#include <vector>
#include "service-bluetooth/SettingsHolder.hpp"

struct HCI;



@@ 19,7 20,7 @@ struct HCI;

namespace Bt
{
    enum Message : uint8_t
    enum Message : std::uint8_t
    {
        /// asynchronous messages to use on event from irq
        EvtSending,        /// Bt stack ordered a write transaction and it is pending


@@ 33,6 34,18 @@ namespace Bt
        EvtErrorRec,       /// there was error o queue receive
    };

    enum Command : std::uint8_t
    {
        StartScan,
        StopScan,
        VisibilityOn,
        VisibilityOff,
        ConnectAudio,
        DisconnectAudio,
        PowerOn,
        PowerOff,
    };

    inline const char *MessageCstr(Message what)
    {
        switch (what) {


@@ 70,8 83,9 @@ class BluetoothWorker : private sys::Worker
        queueService = 0,
        queueControl = 1,
        queueIO_handle, /// bsp support queue
        queue_profiles, /// queue for communication between profile workers,
                        /// main bt_worker_task should dispatch these in events
                        //        queue_profiles, /// queue for communication between profile workers,
                        //                        /// main bt_worker_task should dispatch these in events
        queueCommands,
    };

    TaskHandle_t bt_worker_task = nullptr;


@@ 91,11 105,13 @@ class BluetoothWorker : private sys::Worker

    virtual bool handleMessage(uint32_t queueID);

    bool handleCommand(QueueHandle_t queue);

    bool run();

    bool scan();

    bool toggleVisibility();
    void setVisibility(bool visibility);

    bool start_pan();



@@ 111,4 127,6 @@ class BluetoothWorker : private sys::Worker
    void initAudioBT();

    std::shared_ptr<Bt::Profile> currentProfile;
    std::shared_ptr<Bluetooth::SettingsHolder> settings;
    std::vector<Devicei> pairedDevices;
};

M module-bluetooth/Bluetooth/BtCommand.hpp => module-bluetooth/Bluetooth/BtCommand.hpp +1 -0
@@ 17,6 17,7 @@ namespace Bt
    auto run_stack(TaskHandle_t *handle) -> Error;
    namespace GAP
    {
        extern std::vector<Devicei> devices;
        /// THIS have to be called prior to Bt system start!
        auto register_scan() -> Error;
        auto scan() -> Error;

M module-bluetooth/Bluetooth/BtKeysStorage.cpp => module-bluetooth/Bluetooth/BtKeysStorage.cpp +33 -56
@@ 2,59 2,22 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <algorithm>
#include <cstdio>
#include <filesystem>
#include <purefs/filesystem_paths.hpp>
#include <gsl/gsl_util>

#include "BtKeysStorage.hpp"

json11::Json Bt::KeyStorage::fileJson = json11::Json();
json11::Json Bt::KeyStorage::keysJson = json11::Json();
btstack_link_key_db_t Bt::KeyStorage::keyStorage;
json11::Json::array Bt::KeyStorage::keys;
std::string Bt::KeyStorage::fileContent;
std::string Bt::KeyStorage::keysEntry;
std::shared_ptr<Bluetooth::SettingsHolder> Bt::KeyStorage::settings = nullptr;

namespace Bt
{
    namespace
    {
        std::string loadFilesAsString(const std::filesystem::path &fileToLoad)
        {
            using namespace std::string_literals;
            static constexpr auto file_size_limit = 512LU * 1024LU;
            std::error_code ec;
            auto filesize = std::filesystem::file_size(fileToLoad, ec);
            if (ec || filesize > file_size_limit) {
                return ""s;
            }
            std::string contents(filesize, '\0');
            auto fp = fopen(fileToLoad.c_str(), "r");
            if (!fp) {
                return ""s;
            }
            auto cleanup      = gsl::finally([fp] { fclose(fp); });
            const auto nitems = std::fread(contents.data(), contents.size(), 1, fp);
            return (nitems == 1) ? contents : ""s;
        }

        bool replaceWithString(const std::filesystem::path &fileToModify, const std::string &stringToWrite)
        {
            auto fp = std::fopen(fileToModify.c_str(), "w");
            if (!fp)
                return false;
            auto cleanup       = gsl::finally([fp] { fclose(fp); });
            size_t dataWritten = std::fwrite(stringToWrite.data(), stringToWrite.size(), 1, fp);
            return dataWritten == 1;
        }
    } // namespace

    namespace strings
    {
        inline std::string keysFilename = purefs::dir::getUserDiskPath() / "btkeys.json";
        inline std::string keys         = "keys";
        inline std::string link_key     = "link_key";
        inline std::string bd_addr      = "bd_addr";
        inline std::string type         = "type";
        constexpr inline auto keys     = "keys";
        constexpr inline auto link_key = "link_key";
        constexpr inline auto bd_addr  = "bd_addr";
        constexpr inline auto type     = "type";
    } // namespace strings

    auto KeyStorage::getKeyStorage() -> btstack_link_key_db_t *


@@ 75,28 38,33 @@ namespace Bt
    void KeyStorage::openStorage()
    {
        LOG_INFO("opening storage from API");
        fileContent.clear();
        fileContent = loadFilesAsString(strings::keysFilename);
        if (fileContent.empty()) {
            LOG_WARN("opening empty key file!");
        if (settings) {
            keysEntry = std::visit(Bluetooth::StringVisitor(), settings->getValue(Bluetooth::Settings::BtKeys));
        }
        else {
            LOG_ERROR("failed opening settings for BT!");
            return;
        }
        if (keysEntry.empty()) {
            LOG_WARN("opening empty key entry!");
            return;
        }

        std::string err;
        fileJson = json11::Json::parse(fileContent.c_str(), err);
        keysJson = json11::Json::parse(keysEntry.c_str(), err);
        if (!err.empty()) {
            LOG_ERROR("Error while parsing json: %s", err.c_str());
            return;
        }

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

    void KeyStorage::closeStorage()
    {
        LOG_INFO("closing storage from API");
        writeToFile();
        writeSettings();
    }
    //
    auto KeyStorage::getLinkKey(uint8_t *bd_addr, link_key_t link_key, link_key_type_t *type) -> int


@@ 128,7 96,10 @@ namespace Bt
                                             {strings::link_key, std::string(reinterpret_cast<char *>(link_key))},
                                             {strings::type, type}};
        keys.emplace_back(keyEntry);
        writeToFile();
        if (settings->onLinkKeyAdded) {
            settings->onLinkKeyAdded(bd_addr_to_str(bd_addr));
        }
        writeSettings();
        LOG_INFO("keys written to the file");
        LOG_INFO("Keys in file: %d", (int)keys.size());
    }


@@ 146,7 117,7 @@ namespace Bt
        if (keysSize != keys.size()) {
            LOG_INFO("Key successfully deleted");
        }
        writeToFile();
        writeSettings();
    }
    //
    auto KeyStorage::iterator_init(btstack_link_key_iterator_t *it) -> int


@@ 164,11 135,17 @@ namespace Bt
    {}
    void KeyStorage::set_local_bd_addr(bd_addr_t bd_addr)
    {}
    void KeyStorage::writeToFile()
    void KeyStorage::writeSettings()
    {
        json11::Json finalJson = json11::Json::object{{strings::keys, keys}};
        fileContent            = finalJson.dump();
        replaceWithString(strings::keysFilename, fileContent);
        keysEntry              = finalJson.dump();
        if (settings) {
            settings->setValue(Bluetooth::Settings::BtKeys, keysEntry);
        }
        else {
            LOG_ERROR("failed to open settings to write!");
            return;
        }
    }

} // namespace Bt

M module-bluetooth/Bluetooth/BtKeysStorage.hpp => module-bluetooth/Bluetooth/BtKeysStorage.hpp +5 -4
@@ 7,7 7,7 @@
#include <btstack_util.h>

#include <json/json11.hpp>
#include <vfs.hpp>
#include <service-bluetooth/SettingsHolder.hpp>

namespace Bt
{


@@ 16,6 16,7 @@ namespace Bt
    {
      public:
        static auto getKeyStorage() -> btstack_link_key_db_t *;
        static std::shared_ptr<Bluetooth::SettingsHolder> settings;

      private:
        static void openStorage();


@@ 30,11 31,11 @@ namespace Bt
                                      link_key_type_t *type) -> int;
        static void iterator_done(btstack_link_key_iterator_t *it);
        static void set_local_bd_addr(bd_addr_t bd_addr);
        static void writeToFile();
        static json11::Json fileJson;
        static void writeSettings();
        static json11::Json keysJson;
        static btstack_link_key_db_t keyStorage;
        static json11::Json::array keys;
        static std::string fileContent;
        static std::string keysEntry;
    };

} // namespace Bt

M module-bluetooth/Bluetooth/BtstackWorker.cpp => module-bluetooth/Bluetooth/BtstackWorker.cpp +0 -1
@@ 195,7 195,6 @@ namespace Bt

        hci_init(transport, (void *)&config);
        hci_set_link_key_db(KeyStorage::getKeyStorage());

        hci_event_callback_registration.callback = &hci_packet_handler;
        hci_add_event_handler(&hci_event_callback_registration);
        LOG_DEBUG("BT worker run success");

M module-bluetooth/Bluetooth/interface/profiles/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP.cpp +23 -20
@@ 9,6 9,7 @@
#include <log/log.hpp>
#include <service-bluetooth/BluetoothMessage.hpp>
#include <vector>
#include <BtCommand.hpp>

extern "C"
{


@@ 18,8 19,7 @@ extern "C"

btstack_packet_callback_registration_t cb_handler;

std::vector<Devicei> devices;

std::vector<Devicei> Bt::GAP::devices;
static auto start_scan() -> int;
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);



@@ 44,6 44,7 @@ enum STATE state = INIT;

namespace Bt::GAP
{

    static sys::Service *ownerService = nullptr;

    void setOwnerService(sys::Service *service)


@@ 97,6 98,7 @@ namespace Bt::GAP
        }
        return false;
    }

} // namespace Bt::GAP

#define INQUIRY_INTERVAL 5


@@ 109,8 111,8 @@ static auto start_scan() -> int
static auto has_more_remote_name_requests() -> int
{
    int i;
    for (i = 0; i < devices.size(); i++) {
        if (devices[i].state == REMOTE_NAME_REQUEST) {
    for (i = 0; i < Bt::GAP::devices.size(); i++) {
        if (Bt::GAP::devices[i].state == REMOTE_NAME_REQUEST) {
            return 1;
        }
    }


@@ 120,13 122,14 @@ static auto has_more_remote_name_requests() -> int
static void do_next_remote_name_request()
{
    int i;
    for (i = 0; i < devices.size(); i++) {
    for (i = 0; i < Bt::GAP::devices.size(); i++) {
        // remote name request
        if (devices[i].state == REMOTE_NAME_REQUEST) {
            devices[i].state = REMOTE_NAME_INQUIRED;
            LOG_INFO("Get remote name of %s...", bd_addr_to_str(devices[i].address));
            gap_remote_name_request(
                devices[i].address, devices[i].pageScanRepetitionMode, devices[i].clockOffset | 0x8000);
        if (Bt::GAP::devices[i].state == REMOTE_NAME_REQUEST) {
            Bt::GAP::devices[i].state = REMOTE_NAME_INQUIRED;
            LOG_INFO("Get remote name of %s...", bd_addr_to_str(Bt::GAP::devices[i].address));
            gap_remote_name_request(Bt::GAP::devices[i].address,
                                    Bt::GAP::devices[i].pageScanRepetitionMode,
                                    Bt::GAP::devices[i].clockOffset | 0x8000);
            return;
        }
    }


@@ 186,7 189,7 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe

        case GAP_EVENT_INQUIRY_RESULT: {
            gap_event_inquiry_result_get_bd_addr(packet, addr);
            index = getDeviceIndexForAddress(devices, addr);
            index = getDeviceIndexForAddress(Bt::GAP::devices, addr);
            if (index >= 0) {
                break; // already in our list
            }


@@ 215,36 218,36 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packe
            else {
                dev.state = REMOTE_NAME_REQUEST;
            }
            devices.push_back(dev);
            auto msg = std::make_shared<BluetoothScanResultMessage>(devices);
            Bt::GAP::devices.push_back(dev);
            auto msg = std::make_shared<BluetoothScanResultMessage>(Bt::GAP::devices);
            sys::Bus::SendUnicast(msg, "ApplicationSettings", Bt::GAP::ownerService);
            sys::Bus::SendUnicast(msg, "ApplicationSettingsNew", Bt::GAP::ownerService);

        } break;

        case GAP_EVENT_INQUIRY_COMPLETE:
            for (i = 0; i < devices.size(); i++) {
            for (i = 0; i < Bt::GAP::devices.size(); i++) {
                // retry remote name request
                if (devices[i].state == REMOTE_NAME_INQUIRED)
                    devices[i].state = REMOTE_NAME_REQUEST;
                if (Bt::GAP::devices[i].state == REMOTE_NAME_INQUIRED)
                    Bt::GAP::devices[i].state = REMOTE_NAME_REQUEST;
            }
            continue_remote_names();
            break;

        case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: {
            reverse_bd_addr(&packet[3], addr);
            index = getDeviceIndexForAddress(devices, addr);
            index = getDeviceIndexForAddress(Bt::GAP::devices, addr);
            if (index >= 0) {
                if (packet[2] == 0) {
                    LOG_INFO("Name: '%s'", &packet[9]);
                    devices[index].state = REMOTE_NAME_FETCHED;
                    devices[index].name  = reinterpret_cast<char *>(&packet[9]);
                    Bt::GAP::devices[index].state = REMOTE_NAME_FETCHED;
                    Bt::GAP::devices[index].name  = reinterpret_cast<char *>(&packet[9]);
                }
                else {
                    LOG_INFO("Failed to get name: page timeout");
                }
            }
            if (index + 1 == devices.size()) {
            if (index + 1 == Bt::GAP::devices.size()) {
                LOG_INFO("Scanned all");
                state = DONE;
                gap_inquiry_stop();

M module-services/service-bluetooth/CMakeLists.txt => module-services/service-bluetooth/CMakeLists.txt +1 -0
@@ 5,6 5,7 @@ message( "${PROJECT_NAME}  ${CMAKE_CURRENT_LIST_DIR}" )
set(SOURCES
    ServiceBluetooth.cpp
    service-bluetooth/SettingsHolder.cpp
    service-bluetooth/SettingsSerializer.cpp
)

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

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +62 -21
@@ 15,6 15,7 @@
#include <service-db/Settings.hpp>
#include "service-bluetooth/messages/Status.hpp"
#include "service-bluetooth/messages/SetStatus.hpp"
#include <service-bluetooth/messages/BondedDevices.hpp>

#include <log/log.hpp>



@@ 24,7 25,7 @@
ServiceBluetooth::ServiceBluetooth() : sys::Service(service::name::bluetooth)
{
    auto settings  = std::make_unique<settings::Settings>(this);
    settingsHolder = std::make_unique<Bluetooth::SettingsHolder>(std::move(settings));
    settingsHolder = std::make_shared<Bluetooth::SettingsHolder>(std::move(settings));
    LOG_INFO("[ServiceBluetooth] Initializing");
}



@@ 39,8 40,54 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()
{
    LOG_ERROR("Bluetooth experimental!");
    worker = std::make_unique<BluetoothWorker>(this);
    btStatus.state      = BluetoothStatus::BluetoothState::On;
    btStatus.visibility = true;

    connect(message::bluetooth::RequestBondedDevices(), [&](sys::Message *msg) {
        auto bondedDevicesStr =
            std::visit(Bluetooth::StringVisitor(), this->settingsHolder->getValue(Bluetooth::Settings::BondedDevices));

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

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

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

        return std::make_shared<message::bluetooth::ResponseStatus>(status);
    });

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

        switch (btStatus.state) {
        case BluetoothStatus::State::On:
            worker->run();
            break;
        case BluetoothStatus::State::Off:
            // TODO
            break;
        default:
            break;
        }

        return std::make_shared<message::bluetooth::ResponseStatus>(btStatus);
    });

    settingsHolder->onStateChange = [this]() {
        auto initialState =
            std::visit(Bluetooth::IntVisitor(), this->settingsHolder->getValue(Bluetooth::Settings::State));
        if (static_cast<BluetoothStatus::State>(initialState) == BluetoothStatus::State::On) {
            this->worker->run();
        }
    };

    return sys::ReturnCodes::Success;
}



@@ 52,17 99,6 @@ sys::ReturnCodes ServiceBluetooth::DeinitHandler()

sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp)
{
    // mock response on message::bluetooth::RequestStatus
    if (auto requestStatusMsg = dynamic_cast<message::bluetooth::RequestStatus *>(msg); nullptr != requestStatusMsg) {
        sys::Bus::SendUnicast(std::make_shared<message::bluetooth::ResponseStatus>(btStatus), msg->sender, this);
    }

    // temporary solution for handling message::bluetooth::SetStatus
    if (auto setStatusMsg = dynamic_cast<message::bluetooth::SetStatus *>(msg); nullptr != setStatusMsg) {
        btStatus = setStatusMsg->getStatus();
        sys::Bus::SendBroadcast(std::make_shared<message::bluetooth::ResponseStatus>(btStatus), this);
    }

    try {
        switch (static_cast<MessageType>(msg->messageType)) {
        case MessageType::BluetoothRequest: {


@@ 81,7 117,7 @@ sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg,
                    return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Failure);
                }
            case BluetoothMessage::StopScan:
                worker->stopScan();
                sendWorkerCommand(Bt::StopScan);
                break;
            case BluetoothMessage::PAN: {
                /// TODO request lwip first...


@@ 97,15 133,17 @@ sys::MessagePointer ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg,
                worker->start_pan();
                //                    }
            } break;
            case BluetoothMessage::Visible:
                worker->toggleVisibility();
                break;
            case BluetoothMessage::Visible: {
                static bool visibility = true;
                worker->setVisibility(visibility);
                visibility = !visibility;
            } break;

            case BluetoothMessage::Play:
                worker->establishAudioConnection();
                sendWorkerCommand(Bt::ConnectAudio);
                break;
            case BluetoothMessage::Stop:
                worker->disconnectAudioConnection();
                sendWorkerCommand(Bt::DisconnectAudio);
                break;

            default:


@@ 139,4 177,7 @@ sys::ReturnCodes ServiceBluetooth::SwitchPowerModeHandler(const sys::ServicePowe
    LOG_ERROR("TODO");
    return sys::ReturnCodes::Success;
}

void ServiceBluetooth::sendWorkerCommand(Bt::Command command)
{
    xQueueSend(workerQueue, &command, portMAX_DELAY);
}

M module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp => module-services/service-bluetooth/service-bluetooth/BluetoothMessage.hpp +1 -1
@@ 21,7 21,7 @@ extern "C"

struct BluetoothStatus
{
    enum class BluetoothState
    enum class State
    {
        On,
        Off,

M module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/service-bluetooth/ServiceBluetooth.hpp +3 -2
@@ 29,9 29,10 @@ class ServiceBluetooth : public sys::Service
    sys::ReturnCodes InitHandler() override;
    sys::ReturnCodes DeinitHandler() override;
    virtual sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;
    void sendWorkerCommand(Bt::Command command);
    QueueHandle_t workerQueue = nullptr;
    std::shared_ptr<Bluetooth::SettingsHolder> settingsHolder;

  private:
    std::unique_ptr<BluetoothWorker> worker;
    std::unique_ptr<Bluetooth::SettingsHolder> settingsHolder;
    BluetoothStatus btStatus; // will be replaced with settings storage introduced in [EGD-4579]
};

M module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp => module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp +17 -9
@@ 2,14 2,14 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SettingsHolder.hpp"
#include <log/log.hpp>
namespace Bluetooth
{
    std::map<Settings, std::string> SettingsHolder::settingString{
        {Settings::DeviceName, settings::Bluetooth::deviceName},
        {Settings::Visibility, settings::Bluetooth::deviceVisibility},
        {Settings::Power, settings::Bluetooth::state},
        {Settings::State, settings::Bluetooth::state},
        {Settings::BondedDevices, settings::Bluetooth::bondedDevices},
        {Settings::BtKeys, settings::Bluetooth::btKeys},
    };

    auto SettingsHolder::getValue(const Settings setting) -> SettingEntry


@@ 21,25 21,33 @@ namespace Bluetooth
        settingsMap[newSetting] = value;

        settingsProvider->setValue(settingString[newSetting], std::visit(StringVisitor(), value));
        LOG_INFO("setting %s set: %s", settingString[newSetting].c_str(), std::visit(StringVisitor(), value).c_str());
    }
    SettingsHolder::SettingsHolder(std::unique_ptr<settings::Settings> settingsPtr)
        : settingsProvider(std::move(settingsPtr))
    {
        settingsProvider->registerValueChange(settings::Bluetooth::state, [this](std::string value) {
            setValue(Settings::Power, value);
            this->settingsProvider->unregisterValueChange(settings::Bluetooth::state);
        });
        settingsProvider->registerValueChange(settings::Bluetooth::deviceVisibility, [this](std::string value) {
            setValue(Settings::Visibility, value);
            this->settingsProvider->unregisterValueChange(settings::Bluetooth::deviceVisibility);
            settingsProvider->unregisterValueChange(settings::Bluetooth::deviceVisibility);
        });
        settingsProvider->registerValueChange(settings::Bluetooth::deviceName, [this](std::string value) {
            setValue(Settings::DeviceName, value);
            this->settingsProvider->unregisterValueChange(settings::Bluetooth::deviceName);
            settingsProvider->unregisterValueChange(settings::Bluetooth::deviceName);
        });
        settingsProvider->registerValueChange(settings::Bluetooth::bondedDevices, [this](std::string value) {
            setValue(Settings::BondedDevices, value);
            this->settingsProvider->unregisterValueChange(settings::Bluetooth::bondedDevices);
            settingsProvider->unregisterValueChange(settings::Bluetooth::bondedDevices);
        });
        settingsProvider->registerValueChange(settings::Bluetooth::btKeys, [this](std::string value) {
            setValue(Settings::BtKeys, value);
            settingsProvider->unregisterValueChange(settings::Bluetooth::btKeys);
        });
        settingsProvider->registerValueChange(settings::Bluetooth::state, [this](std::string value) {
            setValue(Settings::State, value);
            settingsProvider->unregisterValueChange(settings::Bluetooth::state);
            if (onStateChange) {
                onStateChange();
            }
        });
    }
} // namespace Bluetooth

M module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp => module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp +41 -3
@@ 1,12 1,13 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <json/json11.hpp>
#include <string>
#include <variant>
#include <service-db/Settings.hpp>
#include <agents/settings/SystemSettings.hpp>
#include <module-utils/Utils.hpp>
#include "SettingsSerializer.hpp"

namespace Bluetooth
{


@@ 15,8 16,9 @@ namespace Bluetooth
    {
        DeviceName,
        Visibility,
        Power,
        BondedDevices
        State,
        BondedDevices,
        BtKeys
    };

    constexpr inline auto trueStr  = "true";


@@ 39,12 41,48 @@ namespace Bluetooth
        }
    };

    struct IntVisitor
    {
        auto operator()(const std::string &input) const -> int
        {
            int value;
            utils::toNumeric(input, value);
            return value;
        }
        auto operator()(bool input) const -> int
        {
            return input;
        }
        auto operator()(int input) const -> int
        {
            return input;
        }
    };

    struct BoolVisitor
    {
        auto operator()(const std::string &input) const -> bool
        {
            return input == trueStr;
        }
        auto operator()(bool input) const -> bool
        {
            return input;
        }
        auto operator()(int input) const -> bool
        {
            return input != 0;
        }
    };

    class SettingsHolder
    {
      public:
        explicit SettingsHolder(std::unique_ptr<settings::Settings> settingsPtr);
        auto getValue(const Settings setting) -> SettingEntry;
        void setValue(const Settings &newSetting, const SettingEntry &value);
        std::function<void()> onStateChange;
        std::function<void(std::string addr)> onLinkKeyAdded;

      private:
        static std::map<Settings, std::string> settingString;

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

#include "SettingsSerializer.hpp"
#include "btstack_util.h"
#include <log/log.hpp>

namespace strings
{
    constexpr inline auto addr    = "addr";
    constexpr inline auto name    = "name";
    constexpr inline auto devices = "devices";
} // namespace strings

auto SettingsSerializer::toString(const std::vector<Devicei> &devices) -> std::string
{
    json11::Json::array devicesJson;
    for (auto &device : devices) {
        auto deviceEntry =
            json11::Json::object{{strings::addr, bd_addr_to_str(device.address)}, {strings::name, device.name}};
        devicesJson.emplace_back(deviceEntry);
    }
    json11::Json finalJson = json11::Json::object{{strings::devices, devicesJson}};

    return finalJson.dump();
}
auto SettingsSerializer::fromString(const std::string &jsonStr) -> std::vector<Devicei>
{
    json11::Json devicesJson;
    std::string err;
    devicesJson = json11::Json::parse(jsonStr.c_str(), err);
    if (!err.empty()) {
        LOG_ERROR("Failed parsing device string!");
        return std::vector<Devicei>();
    }
    json11::Json::array devicesArray;
    devicesArray = std::move(devicesJson[strings::devices].array_items());

    std::vector<Devicei> devicesVector;
    for (auto &device : devicesArray) {
        Devicei temp;
        sscanf_bd_addr(device[strings::addr].string_value().c_str(), temp.address);
        temp.name = device[strings::name].string_value();
        devicesVector.emplace_back(temp);
    }
    return devicesVector;
}

A module-services/service-bluetooth/service-bluetooth/SettingsSerializer.hpp => module-services/service-bluetooth/service-bluetooth/SettingsSerializer.hpp +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

#pragma once

#include <json/json11.hpp>
#include <Device.hpp>

class SettingsSerializer
{
  public:
    static auto toString(const std::vector<Devicei> &devices) -> std::string;
    static auto fromString(const std::string &jsonStr) -> std::vector<Devicei>;
};

M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +1 -0
@@ 21,6 21,7 @@ namespace settings
        constexpr inline auto deviceVisibility = "bt_device_visibility";
        constexpr inline auto deviceName       = "bt_device_name";
        constexpr inline auto bondedDevices    = "bt_bonded_devices";
        constexpr inline auto btKeys           = "bt_keys";
    } // namespace Bluetooth

    namespace Cellular

M module-services/service-desktop/endpoints/calllog/CalllogHelper.cpp => module-services/service-desktop/endpoints/calllog/CalllogHelper.cpp +0 -1
@@ 143,7 143,6 @@ auto CalllogHelper::deleteDBEntry(Context &context) -> sys::ReturnCodes
            }
        },
        context);

    query->setQueryListener(std::move(listener));
    DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Calllog, std::move(query));
    return sys::ReturnCodes::Success;

M module-utils/log/Logger.hpp => module-utils/log/Logger.hpp +1 -0
@@ 8,6 8,7 @@
#include "log.hpp"
#include <map>
#include <mutex.hpp>
#include <string>

namespace Log
{