~aleteoryx/muditaos

b6416b15dac3a638d803681fbad8746b1808b6aa — Adam Dobrowolski 4 years ago 80dc483
[EGD-6019] Minimum settings ownership lifetime fixups

added weakptr link to settings and checks
    it wont crash on deinitialized setings now
Pseuto UT are passing
Added:
    - deregistration on Settings destrution
    - weak referencing of Service to not crash Settings on missuse
    - Proxy as initialization parameter to Settings
Unused code removed
Enabled tests to be written for Settings
44 files changed, 772 insertions(+), 1170 deletions(-)

M enabled_unittests
M module-apps/Application.cpp
M module-apps/application-desktop/ApplicationDesktop.cpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/models/FactoryData.cpp
M module-apps/application-settings-new/models/FactoryData.hpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-audio/ServiceAudio.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp
M module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-db/CMakeLists.txt
M module-services/service-db/EntryPath.cpp
M module-services/service-db/agents/settings/Settings.cpp
M module-services/service-db/agents/settings/SettingsAgent.cpp
M module-services/service-db/agents/settings/SettingsAgent.hpp
M module-services/service-db/agents/settings/SettingsCache.cpp
A module-services/service-db/agents/settings/SettingsProxy.cpp
A module-services/service-db/service-db/EntryPath.hpp
M module-services/service-db/service-db/Settings.hpp
M module-services/service-db/service-db/SettingsCache.hpp
M module-services/service-db/service-db/SettingsMessages.hpp
A module-services/service-db/service-db/SettingsProxy.hpp
M module-services/service-db/test/CMakeLists.txt
M module-services/service-db/test/test-service-db-settings-messages.cpp
A module-services/service-db/test/test-settings-Settings/CMakeLists.txt
A module-services/service-db/test/test-settings-Settings/main.cpp
A module-services/service-db/test/test-settings-Settings/test-settings-Interface.cpp
A module-services/service-db/test/test-settings-Settings/test-settings-Settings.cpp
M module-services/service-db/test/test-settings/CMakeLists.txt
M module-services/service-db/test/test-settings/test-service-db-settings-api.cpp
M module-services/service-db/test/test-settings/test-service-db-settings-testapps.hpp
M module-services/service-db/test/test-settings/test-service-db-settings-testservices.hpp
M module-services/service-desktop/ServiceDesktop.cpp
M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp
M module-services/service-evtmgr/EventManager.cpp
M module-services/service-evtmgr/service-evtmgr/EventManager.hpp
M module-sys/Service/Common.hpp
M module-sys/Service/Service.hpp
A module-sys/Service/ServiceForward.hpp
A module-sys/Service/ServiceProxy.hpp
A module-utils/Split.hpp
M module-utils/Utils.hpp
M enabled_unittests => enabled_unittests +10 -0
@@ 479,5 479,15 @@ TESTS_LIST["catch2-unittest_CellularResult"]="
    CellularResult;
"
#---------
TESTS_LIST["catch2-settings-Settings"]="
    Settings - not initialized;
    Settings - initialized;
    Interface - not initialized;
    Interface - initialized;
    Interface - expired;
    Interface - copied;
"
#---------




M module-apps/Application.cpp => module-apps/Application.cpp +3 -2
@@ 102,7 102,7 @@ namespace app
          default_window(gui::name::window::main_window), windowsStack(this),
          keyTranslator{std::make_unique<gui::KeyInputSimpleTranslation>()}, startInBackground{startInBackground},
          callbackStorage{std::make_unique<CallbackStorage>()}, topBarManager{std::make_unique<TopBarManager>()},
          settings(std::make_unique<settings::Settings>(this)), phoneMode{mode}, phoneLockSubject(this)
          settings(std::make_unique<settings::Settings>()), phoneMode{mode}, phoneLockSubject(this)
    {
        topBarManager->enableIndicators({gui::top_bar::Indicator::Time});
        using TimeMode = gui::top_bar::TimeConfiguration::TimeMode;


@@ 154,7 154,6 @@ namespace app
    Application::~Application() noexcept
    {
        windowsStack.windows.clear();
        settings->unregisterValueChange();
    }

    Application::State Application::getState()


@@ 621,6 620,7 @@ namespace app
    {
        setState(State::INITIALIZING);

        settings->init(service::ServiceProxy(shared_from_this()));
        app::manager::Controller::applicationInitialised(this, StartupStatus::Success, startInBackground);

        if (startInBackground) {


@@ 638,6 638,7 @@ namespace app

    sys::ReturnCodes Application::DeinitHandler()
    {
        settings->deinit();
        LOG_INFO("Closing an application: %s", GetName().c_str());

        for (const auto &[windowName, window] : windowsStack) {

M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +0 -2
@@ 383,8 383,6 @@ namespace app

    sys::ReturnCodes ApplicationDesktop::DeinitHandler()
    {
        LOG_INFO("DeinitHandler");
        settings->unregisterValueChange();
        return sys::ReturnCodes::Success;
    }


M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +1 -1
@@ 460,7 460,7 @@ namespace app
            return std::make_unique<gui::CertificationWindow>(app);
        });
        windowsFactory.attach(gui::window::name::technical_information, [&](Application *app, const std::string &name) {
            auto factoryData = std::make_unique<FactoryData>(std::make_unique<::settings::Settings>(this));
            auto factoryData = std::make_unique<FactoryData>(*settings);
            auto presenter   = std::make_unique<TechnicalWindowPresenter>(std::move(factoryData));
            return std::make_unique<gui::TechnicalInformationWindow>(app, std::move(presenter));
        });

M module-apps/application-settings-new/models/FactoryData.cpp => module-apps/application-settings-new/models/FactoryData.cpp +7 -7
@@ 5,28 5,28 @@
#include <service-db/Settings.hpp>
#include <service-db/agents/settings/FactorySettings.hpp>

FactoryData::FactoryData(std::unique_ptr<settings::Settings> settingsProvider) : settings(std::move(settingsProvider))
FactoryData::FactoryData(settings::Settings &settings) : settings(settings)
{}

auto FactoryData::getModel() -> std::string
{
    return settings->getValue(settings::factory::entry_key + std::string("/model"), settings::SettingsScope::Global);
    return settings.getValue(settings::factory::entry_key + std::string("/model"), settings::SettingsScope::Global);
}
auto FactoryData::getCase() -> std::string
{
    return settings->getValue(settings::factory::entry_key + std::string("/case"), ::settings::SettingsScope::Global);
    return settings.getValue(settings::factory::entry_key + std::string("/case"), ::settings::SettingsScope::Global);
}
auto FactoryData::getSerial() -> std::string
{
    return settings->getValue(settings::factory::entry_key + std::string("/serial"), settings::SettingsScope::Global);
    return settings.getValue(settings::factory::entry_key + std::string("/serial"), settings::SettingsScope::Global);
}
auto FactoryData::getBatteryRev() -> std::string
{
    return settings->getValue(settings::factory::entry_key + std::string("/battery_revision"),
                              settings::SettingsScope::Global);
    return settings.getValue(settings::factory::entry_key + std::string("/battery_revision"),
                             settings::SettingsScope::Global);
}
auto FactoryData::getPcb(std::string type) -> std::string
{
    std::string full_pcb = "/pcb_" + type + "_version";
    return settings->getValue(settings::factory::entry_key + full_pcb, settings::SettingsScope::Global);
    return settings.getValue(settings::factory::entry_key + full_pcb, settings::SettingsScope::Global);
}

M module-apps/application-settings-new/models/FactoryData.hpp => module-apps/application-settings-new/models/FactoryData.hpp +2 -2
@@ 20,7 20,7 @@ class AbstractFactoryData
class FactoryData : public AbstractFactoryData
{
  public:
    explicit FactoryData(std::unique_ptr<settings::Settings> settings);
    explicit FactoryData(settings::Settings &settings);

    auto getModel() -> std::string override;
    auto getCase() -> std::string override;


@@ 29,5 29,5 @@ class FactoryData : public AbstractFactoryData
    auto getPcb(std::string type) -> std::string override;

  private:
    std::unique_ptr<settings::Settings> settings;
    settings::Settings &settings;
};

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +3 -2
@@ 134,7 134,7 @@ namespace app::manager
        : Service{serviceName, {}, ApplicationManagerStackDepth},
          ApplicationManagerBase(std::move(launchers)), rootApplicationName{_rootApplicationName},
          actionsRegistry{[this](ActionEntry &action) { return handleAction(action); }}, notificationProvider(this),
          autoLockEnabled(false), settings(std::make_unique<settings::Settings>(this)),
          autoLockEnabled(false), settings(std::make_unique<settings::Settings>()),
          phoneModeObserver(std::make_unique<sys::phone_modes::Observer>()),
          phoneLockHandler(locks::PhoneLockHandler(this))
    {


@@ 148,6 148,7 @@ namespace app::manager

    sys::ReturnCodes ApplicationManager::InitHandler()
    {
        settings->init(service::ServiceProxy(shared_from_this()));
        utils::setDisplayLanguage(
            settings->getValue(settings::SystemProperties::displayLanguage, settings::SettingsScope::Global));



@@ 259,7 260,7 @@ namespace app::manager

    sys::ReturnCodes ApplicationManager::DeinitHandler()
    {
        settings->unregisterValueChange();
        settings->deinit();
        closeApplications();
        return sys::ReturnCodes::Success;
    }

M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +3 -1
@@ 94,7 94,7 @@ static constexpr std::initializer_list<std::pair<audio::DbPathElement, const cha
ServiceAudio::ServiceAudio()
    : sys::Service(service::name::audio, "", audioServiceStackSize, sys::ServicePriority::Idle),
      audioMux([this](auto... params) { return this->AudioServicesCallback(params...); }),
      settingsProvider(std::make_unique<settings::Settings>(this))
      settingsProvider(std::make_unique<settings::Settings>())
{
    LOG_INFO("[ServiceAudio] Initializing");
    bus.channels.push_back(sys::BusChannel::ServiceAudioNotifications);


@@ 110,6 110,7 @@ ServiceAudio::~ServiceAudio()

sys::ReturnCodes ServiceAudio::InitHandler()
{
    settingsProvider->init(service::ServiceProxy(shared_from_this()));
    std::transform(std::begin(cacheInitializer),
                   std::end(cacheInitializer),
                   std::inserter(settingsCache, std::end(settingsCache)),


@@ 125,6 126,7 @@ sys::ReturnCodes ServiceAudio::InitHandler()

sys::ReturnCodes ServiceAudio::DeinitHandler()
{
    settingsProvider->deinit();
    return sys::ReturnCodes::Success;
}


M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +6 -3
@@ 45,9 45,6 @@ namespace

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



@@ 60,6 57,11 @@ ServiceBluetooth::~ServiceBluetooth()
// this means it is an init point of bluetooth feature handling
sys::ReturnCodes ServiceBluetooth::InitHandler()
{
    auto settings = std::make_unique<settings::Settings>();
    settings->init(service::ServiceProxy(shared_from_this()));
    settingsHolder                  = std::make_shared<bluetooth::SettingsHolder>(std::move(settings));
    bluetooth::KeyStorage::settings = settingsHolder;

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



@@ 105,6 107,7 @@ sys::ReturnCodes ServiceBluetooth::InitHandler()

sys::ReturnCodes ServiceBluetooth::DeinitHandler()
{
    settingsHolder->deinit();
    worker->deinit();
    return sys::ReturnCodes::Success;
}

M module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp => module-services/service-bluetooth/service-bluetooth/SettingsHolder.cpp +5 -0
@@ 53,4 53,9 @@ namespace bluetooth
            }
        });
    }

    void SettingsHolder::deinit()
    {
        settingsProvider->deinit();
    }
} // namespace Bluetooth

M module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp => module-services/service-bluetooth/service-bluetooth/SettingsHolder.hpp +1 -0
@@ 84,6 84,7 @@ namespace bluetooth
        void setValue(const Settings &newSetting, const SettingEntry &value);
        std::function<void()> onStateChange;
        std::function<void(const std::string &)> onLinkKeyAdded;
        void deinit();

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

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +15 -13
@@ 180,15 180,6 @@ ServiceCellular::ServiceCellular()
    bus.channels.push_back(sys::BusChannel::ServiceEvtmgrNotifications);
    bus.channels.push_back(sys::BusChannel::PhoneModeChanges);

    settings = std::make_unique<settings::Settings>(this);

    connectionManager = std::make_unique<ConnectionManager>(
        utils::getNumericValue<bool>(
            settings->getValue(settings::Cellular::offlineMode, settings::SettingsScope::Global)),
        static_cast<std::chrono::minutes>(utils::getNumericValue<int>(settings->getValue(
            settings->getValue(settings::Offline::connectionFrequency, settings::SettingsScope::Global)))),
        std::make_shared<ConnectionManagerCellularCommands>(*this));

    callStateTimer = sys::TimerFactory::createPeriodicTimer(
        this, "call_state", std::chrono::milliseconds{1000}, [this](sys::Timer &) { CallStateTimerHandler(); });
    stateTimer = sys::TimerFactory::createPeriodicTimer(


@@ 201,7 192,10 @@ ServiceCellular::ServiceCellular()
        sys::TimerFactory::createPeriodicTimer(this, "connection", std::chrono::seconds{60}, [this](sys::Timer &) {
            utility::conditionally_invoke(
                [this]() { return phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Offline); },
                [this]() { connectionManager->onTimerTick(); });
                [this]() {
                    if (connectionManager != nullptr)
                        connectionManager->onTimerTick();
                });
        });

    ongoingCall.setStartCallAction([=](const CalllogRecord &rec) {


@@ 248,8 242,6 @@ ServiceCellular::ServiceCellular()
ServiceCellular::~ServiceCellular()
{
    LOG_INFO("[ServiceCellular] Cleaning resources");
    settings->unregisterValueChange(settings::Cellular::volte_on, ::settings::SettingsScope::Global);
    settings->unregisterValueChange(settings::Cellular::apn_list, ::settings::SettingsScope::Global);
}

// this static function will be replaced by Settings API


@@ 284,6 276,16 @@ sys::ReturnCodes ServiceCellular::InitHandler()
{
    board = EventManagerServiceAPI::GetBoard(this);

    settings = std::make_unique<settings::Settings>();
    settings->init(service::ServiceProxy(shared_from_this()));

    connectionManager = std::make_unique<ConnectionManager>(
        utils::getNumericValue<bool>(
            settings->getValue(settings::Cellular::offlineMode, settings::SettingsScope::Global)),
        static_cast<std::chrono::minutes>(utils::getNumericValue<int>(settings->getValue(
            settings->getValue(settings::Offline::connectionFrequency, settings::SettingsScope::Global)))),
        std::make_shared<ConnectionManagerCellularCommands>(*this));

    state.set(this, State::ST::WaitForStartPermission);
    settings->registerValueChange(
        settings::Cellular::volte_on,


@@ 307,7 309,7 @@ sys::ReturnCodes ServiceCellular::InitHandler()

sys::ReturnCodes ServiceCellular::DeinitHandler()
{

    settings->deinit();
    return sys::ReturnCodes::Success;
}


M module-services/service-db/CMakeLists.txt => module-services/service-db/CMakeLists.txt +1 -0
@@ 15,6 15,7 @@ set(SOURCES
    messages/QueryMessage.cpp
    agents/settings/SettingsAgent.cpp
    agents/settings/Settings.cpp
    agents/settings/SettingsProxy.cpp
    agents/settings/SettingsCache.cpp
    agents/settings/FactorySettings.cpp
    agents/file_indexer/FileIndexerAgent.cpp

M module-services/service-db/EntryPath.cpp => module-services/service-db/EntryPath.cpp +19 -1
@@ 1,10 1,28 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "service-db/SettingsMessages.hpp"
#include "service-db/EntryPath.hpp"
#include <Split.hpp>

namespace settings
{

    void EntryPath::parse(const std::string &dbPath)
    {
        auto parts = utils::split(dbPath, "\\", false);
        if (1 == parts.size()) {
            variable = dbPath;
            scope    = SettingsScope::Global;
        }
        else {
            mode     = parts[0];
            service  = parts[1];
            profile  = parts[2];
            variable = parts[3];
            scope    = SettingsScope::AppLocal;
        }
    }

    namespace
    {
        bool isLessAppLocal(const EntryPath &lhs, const EntryPath &rhs) noexcept

M module-services/service-db/agents/settings/Settings.cpp => module-services/service-db/agents/settings/Settings.cpp +50 -211
@@ 2,18 2,15 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-db/Settings.hpp>
#include <service-db/SettingsMessages.hpp>
#include <service-db/SettingsCache.hpp>

#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <log/log.hpp>

#include <memory>
#include <stdexcept>
#include <utility>
#include <vector>

#if defined(DEBUG_SETTINGS_DB) and DEBUG_SETTINGS_DB == 1
#include <log/log.hpp>
#define log_debug(...) LOG_DEBUG(__VA_ARGS__);
#else
#define log_debug(...)


@@ 21,259 18,101 @@

namespace settings
{
    Settings::Settings(sys::Service *app, const std::string &dbAgentName, SettingsCache *cache)
        : dbAgentName(dbAgentName), cache(cache)
    Settings::~Settings()
    {
        this->app =
            std::shared_ptr<sys::Service>(app, [](sys::Service *service) {}); /// with deleter that doesn't delete.
        this->app->bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
        if (nullptr == cache) {
            this->cache = SettingsCache::getInstance();
        }
        registerHandlers();
        deinit();
    }

    void Settings::sendMsg(std::shared_ptr<settings::Messages::SettingsMessage> &&msg)
    void Settings::init(const service::ServiceProxy &interface)
    {
        app->bus.sendUnicast(std::move(msg), dbAgentName);
        this->interface = SettingsProxy(interface);
        this->interface.init([&](const EntryPath &p, const std::string &v) { this->handleVariableChanged(p, v); });
        if (not interface.isValid()) {
            throw std::invalid_argument("need the interface!");
        }
    }

    void Settings::registerHandlers()
    {
        using std::placeholders::_1;
        using std::placeholders::_2;
        log_debug("Settings::registerHandlers for %s", app->GetName().c_str());
        app->connect(settings::Messages::VariableChanged(), std::bind(&Settings::handleVariableChanged, this, _1));
        app->connect(settings::Messages::CurrentProfileChanged(),
                     std::bind(&Settings::handleCurrentProfileChanged, this, _1));
        app->connect(settings::Messages::CurrentModeChanged(),
                     std::bind(&Settings::handleCurrentModeChanged, this, _1));
        app->connect(settings::Messages::ProfileListResponse(),
                     std::bind(&Settings::handleProfileListResponse, this, _1));
        app->connect(settings::Messages::ModeListResponse(), std::bind(&Settings::handleModeListResponse, this, _1));
    }
    auto Settings::handleVariableChanged(sys::Message *req) -> sys::MessagePointer
    void Settings::deinit()
    {
        log_debug("handleVariableChanged");
        if (auto msg = dynamic_cast<settings::Messages::VariableChanged *>(req)) {
            auto key = msg->getPath();
            auto val = msg->getValue();
            log_debug("handleVariableChanged: (k=v): (%s=%s)", key.to_string().c_str(), val.value_or("").c_str());
            ValueCb::iterator it_cb = cbValues.find(key);
            if (cbValues.end() != it_cb) {
                auto [cb, cbWithName] = it_cb->second;
                if (nullptr != cb && nullptr != cbWithName) {
                    // in case of two callbacks there is a need to duplicate the value
                    auto value = val.value_or("");
                    cb(std::string{value});
                    cbWithName(key.variable, value);
                }
                else if (nullptr != cb) {
                    cb(val.value_or(""));
                }
                else {
                    cbWithName(key.variable, val.value_or(""));
                }
        if (interface.isValid()) {
            interface.deinit();
            for (const auto &callbacks : cbValues) {
                log_debug("[Settings::unregisterValueChange] %s", callbacks.first.to_string().c_str());
                interface.unregisterValueChange(callbacks.first);
            }
        }
        return std::make_shared<sys::ResponseMessage>();
    }
    auto Settings::handleCurrentProfileChanged(sys::Message *req) -> sys::MessagePointer
    {
        log_debug("handleCurrentProfileChanged");
        if (auto msg = dynamic_cast<settings::Messages::CurrentProfileChanged *>(req)) {
            auto profile = msg->getProfileName();
            log_debug("handleCurrentProfileChanged: %s", profile.c_str());
            cbProfile(profile);
        }
        return std::make_shared<sys::ResponseMessage>();
    }
    auto Settings::handleCurrentModeChanged(sys::Message *req) -> sys::MessagePointer
    {
        log_debug("handleCurrentModeChanged");
        if (auto msg = dynamic_cast<settings::Messages::CurrentModeChanged *>(req)) {
            auto mode = msg->getProfileName();
            log_debug("handleCurrentModeChanged: %s", mode.c_str());
            cbMode(mode);
        }
        return std::make_shared<sys::ResponseMessage>();
    }
    auto Settings::handleProfileListResponse(sys::Message *req) -> sys::MessagePointer
    {
        log_debug("handleProfileListResponse");
        if (auto msg = dynamic_cast<settings::Messages::ProfileListResponse *>(req)) {
            auto profiles = msg->getValue();
            log_debug("handleProfileListResponse: %zu elements", profiles.size());
            cbAllProfiles(profiles);
        }
        return std::make_shared<sys::ResponseMessage>();
        cbValues.clear();
    }
    auto Settings::handleModeListResponse(sys::Message *req) -> sys::MessagePointer

    void Settings::handleVariableChanged(const EntryPath &path, const std::string &value)
    {
        log_debug("handleModeListResponse");
        if (auto msg = dynamic_cast<settings::Messages::ModeListResponse *>(req)) {
            auto modes = msg->getValue();
            log_debug("handleModeListResponse: %zu elements", modes.size());
            cbAllModes(modes);
        log_debug("handleVariableChanged: (k=v): (%s=%s)", path.to_string().c_str(), value.c_str());
        if (auto callbacks = cbValues.find(path); std::end(cbValues) != callbacks) {
            auto &[cb, cbWithName] = callbacks->second;
            if (nullptr != cb) {
                cb(value);
            }
            if (nullptr != cbWithName) {
                cbWithName(path.variable, value);
            }
        }
        return std::make_shared<sys::ResponseMessage>();
    }

    void Settings::registerValueChange(const std::string &variableName, ValueChangedCallback cb, SettingsScope scope)
    {
        EntryPath path;
        path.variable = variableName;
        path.service  = app->GetName();
        path.scope    = scope;
        auto path = EntryPath{.service = interface.ownerName(), .variable = variableName, .scope = scope};

        auto it_cb = cbValues.find(path);
        if (cbValues.end() != it_cb && nullptr != it_cb->second.first) {
            LOG_INFO("Callback function on value change (%s) already exists, rewriting", path.to_string().c_str());
        if (auto callbacks = cbValues.find(path);
            std::end(cbValues) != callbacks && nullptr != callbacks->second.first) {
            log_debug("Callback function on value change (%s) already exists, rewriting", path.to_string().c_str());
        }
        cbValues[path].first = std::move(cb);

        auto msg = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        sendMsg(std::move(msg));
        interface.registerValueChange(std::move(path));
    }

    void Settings::registerValueChange(const std::string &variableName,
                                       ValueChangedCallbackWithName cb,
                                       SettingsScope scope)
    {
        EntryPath path;
        path.variable = variableName;
        path.service  = app->GetName();
        path.scope    = scope;
        auto path = EntryPath{.service = interface.ownerName(), .variable = variableName, .scope = scope};

        auto it_cb = cbValues.find(path);
        if (cbValues.end() != it_cb && nullptr != it_cb->second.second) {
            LOG_INFO("Callback function on value change (%s) already exists, rewriting", path.to_string().c_str());
        if (auto callbacks = cbValues.find(path); cbValues.end() != callbacks && nullptr != callbacks->second.second) {
            log_debug("Callback function on value change (%s) already exists, rewriting", path.to_string().c_str());
        }
        cbValues[path].second = std::move(cb);

        auto msg = std::make_shared<settings::Messages::RegisterOnVariableChange>(path);
        sendMsg(std::move(msg));
        interface.registerValueChange(std::move(path));
    }

    void Settings::unregisterValueChange(const std::string &variableName, SettingsScope scope)
    {
        EntryPath path;
        path.variable = variableName;
        path.service  = app->GetName();
        path.scope    = scope;
        auto path = EntryPath{.service = interface.ownerName(), .variable = variableName, .scope = scope};

        auto it_cb = cbValues.find(path);
        if (cbValues.end() == it_cb) {
            LOG_INFO("Callback function on value change (%s) does not exist", path.to_string().c_str());
        auto callbacks = cbValues.find(path);
        if (cbValues.end() == callbacks) {
            log_debug("Callback function on value change (%s) does not exist", path.to_string().c_str());
        }
        else {
            log_debug("[Settings::unregisterValueChange] %s", path.to_string().c_str());
            cbValues.erase(it_cb);
        }

        auto msg = std::make_shared<settings::Messages::UnregisterOnVariableChange>(path);
        sendMsg(std::move(msg));
    }

    void Settings::unregisterValueChange()
    {
        for (const auto &it_cb : cbValues) {
            log_debug("[Settings::unregisterValueChange] %s", it_cb.first.to_string().c_str());
            auto msg = std::make_shared<settings::Messages::UnregisterOnVariableChange>(it_cb.first);
            sendMsg(std::move(msg));
            cbValues.erase(callbacks);
        }
        cbValues.clear();
        LOG_INFO("Unregistered all settings variable change on application (%s)", app->GetName().c_str());
        interface.unregisterValueChange(std::move(path));
    }

    void Settings::setValue(const std::string &variableName, const std::string &variableValue, SettingsScope scope)
    {
        EntryPath path;
        path.variable = variableName;
        path.service  = app->GetName();
        path.scope    = scope;
        auto msg      = std::make_shared<settings::Messages::SetVariable>(path, variableValue);
        sendMsg(std::move(msg));
        cache->setValue(path, variableValue);
        auto path = EntryPath{.service = interface.ownerName(), .variable = variableName, .scope = scope};
        interface.setValue(path, variableValue);
        getCache()->setValue(path, variableValue);
    }

    std::string Settings::getValue(const std::string &variableName, SettingsScope scope)
    {
        EntryPath path;
        path.variable = variableName;
        path.service  = app->GetName();
        path.scope    = scope;
        return cache->getValue(path);
    }

    void Settings::getAllProfiles(OnAllProfilesRetrievedCallback cb)
    {
        if (nullptr == cbAllProfiles) {
            sendMsg(std::make_shared<settings::Messages::ListProfiles>());
        }
        cbAllProfiles = std::move(cb);
        return getCache()->getValue({.service = interface.ownerName(), .variable = variableName, .scope = scope});
    }

    void Settings::setCurrentProfile(const std::string &profile)
    SettingsCache *Settings::getCache()
    {
        sendMsg(std::make_shared<settings::Messages::SetCurrentProfile>(profile));
        return SettingsCache::getInstance();
    }

    void Settings::addProfile(const std::string &profile)
    {
        sendMsg(std::make_shared<settings::Messages::AddProfile>(profile));
    }

    void Settings::registerProfileChange(ProfileChangedCallback cb)
    {
        if (nullptr != cbProfile) {
            log_debug("Profile change callback already exists, overwritting");
        }
        else {
            sendMsg(std::make_shared<settings::Messages::RegisterOnProfileChange>());
        }

        cbProfile = std::move(cb);
    }

    void Settings::unregisterProfileChange()
    {
        cbProfile = nullptr;
        sendMsg(std::make_shared<settings::Messages::UnregisterOnProfileChange>());
    }

    void Settings::getAllModes(OnAllModesRetrievedCallback cb)
    {
        if (nullptr == cbAllModes) {
            sendMsg(std::make_shared<settings::Messages::ListModes>());
        }
        cbAllModes = std::move(cb);
    }

    void Settings::setCurrentMode(const std::string &mode)
    {
        sendMsg(std::make_shared<settings::Messages::SetCurrentMode>(mode));
    }

    void Settings::addMode(const std::string &mode)
    {
        sendMsg(std::make_shared<settings::Messages::AddMode>(mode));
    }

    void Settings::registerModeChange(ModeChangedCallback cb)
    {
        if (nullptr != cbMode) {
            log_debug("ModeChange callback allready set overwriting");
        }
        else {
            sendMsg(std::make_shared<settings::Messages::RegisterOnModeChange>());
        }
        cbMode = std::move(cb);
    }

    void Settings::unregisterModeChange()
    {
        cbMode = nullptr;
        sendMsg(std::make_shared<settings::Messages::UnregisterOnModeChange>());
    }
} // namespace settings

M module-services/service-db/agents/settings/SettingsAgent.cpp => module-services/service-db/agents/settings/SettingsAgent.cpp +0 -255
@@ 11,12 11,6 @@

namespace settings
{
    namespace DbPaths
    {
        constexpr auto phone_mode    = "system/phone_mode";
        constexpr auto phone_profile = "system/phone_profile";
    } // namespace DbPaths

    namespace factory
    {
        constexpr auto data_file         = "personalization.json";


@@ 71,29 65,6 @@ void SettingsAgent::registerMessages()
                           std::bind(&SettingsAgent::handleRegisterOnVariableChange, this, _1));
    parentService->connect(settings::Messages::UnregisterOnVariableChange(),
                           std::bind(&SettingsAgent::handleUnregisterOnVariableChange, this, _1));
    // profile
    parentService->connect(settings::Messages::RegisterOnProfileChange(),
                           std::bind(&SettingsAgent::handleRegisterProfileChange, this, _1));
    parentService->connect(settings::Messages::UnregisterOnProfileChange(),
                           std::bind(&SettingsAgent::handleUnregisterProfileChange, this, _1));
    parentService->connect(settings::Messages::SetCurrentProfile(),
                           std::bind(&SettingsAgent::handleSetCurrentProfile, this, _1));
    parentService->connect(settings::Messages::GetCurrentProfile(),
                           std::bind(&SettingsAgent::handleGetCurrentProfile, this, _1));
    parentService->connect(settings::Messages::AddProfile(), std::bind(&SettingsAgent::handleAddProfile, this, _1));
    parentService->connect(settings::Messages::ListProfiles(), std::bind(&SettingsAgent::handleListProfiles, this, _1));

    // mode
    parentService->connect(settings::Messages::RegisterOnModeChange(),
                           std::bind(&SettingsAgent::handleRegisterOnModeChange, this, _1));
    parentService->connect(settings::Messages::UnregisterOnModeChange(),
                           std::bind(&SettingsAgent::handleUnregisterOnModeChange, this, _1));
    parentService->connect(settings::Messages::SetCurrentMode(),
                           std::bind(&SettingsAgent::handleSetCurrentMode, this, _1));
    parentService->connect(settings::Messages::GetCurrentMode(),
                           std::bind(&SettingsAgent::handleGetCurrentMode, this, _1));
    parentService->connect(settings::Messages::AddMode(), std::bind(&SettingsAgent::handleAddMode, this, _1));
    parentService->connect(settings::Messages::ListModes(), std::bind(&SettingsAgent::handleListModes, this, _1));
}

auto SettingsAgent::getDbInitString() -> const std::string


@@ 137,89 108,6 @@ auto SettingsAgent::dbUnregisterValueChange(const settings::EntryPath &path) -> 
        settings::Statements::clearNotificationdRow, path.to_string().c_str(), path.service.c_str());
}

// db Profile
auto SettingsAgent::dbRegisterOnProfileChange(const std::string &service) -> bool
{
    return database->execute(settings::Statements::setNotification, settings::DbPaths::phone_profile, service.c_str());
}
auto SettingsAgent::dbUnregisterOnProfileChange(const std::string &service) -> bool
{
    return database->execute(
        settings::Statements::clearNotificationdRow, settings::DbPaths::phone_profile, service.c_str());
}
auto SettingsAgent::dbSetCurrentProfile(const std::string &profile) -> bool
{
    return database->execute(settings::Statements::updateValue, profile.c_str(), settings::DbPaths::phone_profile);
}
auto SettingsAgent::dbGetCurrentProfile() -> std::string
{
    auto qProfile = database->query(settings::Statements::getValue, settings::DbPaths::phone_profile);
    if (nullptr == qProfile || 1 != qProfile->getRowCount()) {
        return std::string{};
    }
    return (*qProfile)[0].getString();
}
auto SettingsAgent::dbGetAllProfiles() -> std::list<std::string>
{
    auto qProfiles = database->query(settings::Statements::getDictValue, settings::DbPaths::phone_profile);
    if (nullptr == qProfiles || 0 == qProfiles->getRowCount()) {
        return std::list<std::string>{};
    }
    std::list<std::string> profiles;
    do {
        profiles.push_back((*qProfiles)[0].getString());
    } while (qProfiles->nextRow());
    return profiles;
}
auto SettingsAgent::dbAddProfile(const std::string &profile) -> bool
{
    return database->execute(settings::Statements::addDictValue, settings::DbPaths::phone_profile, profile.c_str());
}

// dbMode
auto SettingsAgent::dbRegisterOnModeChange(const std::string &service) -> bool
{
    return database->execute(settings::Statements::setNotification, settings::DbPaths::phone_mode, service.c_str());
}

auto SettingsAgent::dbUnregisterOnModeChange(const std::string &service) -> bool
{
    return database->execute(
        settings::Statements::clearNotificationdRow, settings::DbPaths::phone_mode, service.c_str());
}

auto SettingsAgent::dbSetCurrentMode(const std::string &mode) -> bool
{
    return database->execute(settings::Statements::updateValue, mode.c_str(), settings::DbPaths::phone_mode);
}

auto SettingsAgent::dbGetCurrentMode() -> std::string
{
    auto qMode = database->query(settings::Statements::getValue, settings::DbPaths::phone_mode);
    if (nullptr == qMode || 1 != qMode->getRowCount()) {
        return std::string{};
    }
    return (*qMode)[0].getString();
}

auto SettingsAgent::dbGetAllModes() -> std::list<std::string>
{
    auto qModes = database->query(settings::Statements::getDictValue, settings::DbPaths::phone_mode);
    if (nullptr == qModes || 0 == qModes->getRowCount()) {
        return std::list<std::string>{};
    }
    std::list<std::string> modes;
    do {
        modes.push_back((*qModes)[0].getString());
    } while (qModes->nextRow());
    return modes;
}

auto SettingsAgent::dbAddMode(const std::string &mode) -> bool
{
    return database->execute(settings::Statements::addDictValue, settings::DbPaths::phone_mode, mode.c_str());
}

auto SettingsAgent::handleGetVariable(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::GetVariable *>(req)) {


@@ 292,146 180,3 @@ auto SettingsAgent::handleUnregisterOnVariableChange(sys::Message *req) -> sys::
    }
    return std::make_shared<sys::ResponseMessage>();
}

// profile
auto SettingsAgent::handleRegisterProfileChange(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::RegisterOnProfileChange *>(req)) {
        if (dbRegisterOnProfileChange(msg->sender)) {
            profileChangedRecipients.insert(msg->sender);
            auto msgCurrentProfile = std::make_shared<settings::Messages::CurrentProfileChanged>(dbGetCurrentProfile());
            parentService->bus.sendUnicast(std::move(msgCurrentProfile), msg->sender);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleUnregisterProfileChange(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::UnregisterOnProfileChange *>(req)) {
        if (dbUnregisterOnProfileChange(msg->sender)) {
            profileChangedRecipients.erase(msg->sender);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleSetCurrentProfile(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::SetCurrentProfile *>(req)) {
        auto profile = msg->getProfileName();
        if (dbSetCurrentProfile(profile)) {
            for (const auto &service : profileChangedRecipients) {
                if (service != msg->sender) {
                    auto msgProfileChanged = std::make_shared<settings::Messages::CurrentProfileChanged>(profile);
                    parentService->bus.sendUnicast(std::move(msgProfileChanged), service);
                }
            }
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleGetCurrentProfile(sys::Message *req) -> sys::MessagePointer
{
    if (nullptr != dynamic_cast<settings::Messages::GetCurrentProfile *>(req)) {
        auto service = profileChangedRecipients.find(req->sender);
        if (profileChangedRecipients.end() != service) {
            auto msgCurrentProfile = std::make_shared<settings::Messages::CurrentProfileChanged>(dbGetCurrentProfile());
            parentService->bus.sendUnicast(std::move(msgCurrentProfile), *service);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleAddProfile(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::AddProfile *>(req)) {
        if (dbAddProfile(msg->getProfileName())) {
            auto allProfiles = dbGetAllProfiles();
            for (const auto &service : profileChangedRecipients) {
                if (service != req->sender) {
                    auto msgAllProfiles = std::make_shared<settings::Messages::ProfileListResponse>(allProfiles);
                    parentService->bus.sendUnicast(std::move(msgAllProfiles), service);
                }
            }
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleListProfiles(sys::Message *req) -> sys::MessagePointer
{
    if (nullptr != dynamic_cast<settings::Messages::ListProfiles *>(req)) {
        profileChangedRecipients.insert(req->sender);
        auto msgAllProfiles = std::make_shared<settings::Messages::ProfileListResponse>(dbGetAllProfiles());
        parentService->bus.sendUnicast(std::move(msgAllProfiles), req->sender);
    }
    return std::make_shared<sys::ResponseMessage>();
}

// mode
auto SettingsAgent::handleRegisterOnModeChange(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::RegisterOnModeChange *>(req)) {
        if (dbRegisterOnModeChange(msg->sender)) {
            modeChangeRecipients.insert(msg->sender);
            auto msgMode = std::make_shared<settings::Messages::CurrentModeChanged>(dbGetCurrentMode());
            parentService->bus.sendUnicast(std::move(msgMode), msg->sender);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleUnregisterOnModeChange(sys::Message *req) -> sys::MessagePointer
{
    if (nullptr != dynamic_cast<settings::Messages::UnregisterOnModeChange *>(req)) {
        dbUnregisterOnModeChange(req->sender);
        modeChangeRecipients.erase(req->sender);
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleSetCurrentMode(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::SetCurrentMode *>(req)) {
        if (auto newMode = msg->getProfileName(); dbGetCurrentMode() != newMode) {
            if (dbSetCurrentMode(newMode)) {
                for (const auto &service : modeChangeRecipients) {
                    if (service != msg->sender) {
                        auto msgModeChanged = std::make_shared<settings::Messages::CurrentModeChanged>(newMode);
                        parentService->bus.sendUnicast(std::move(msgModeChanged), service);
                    }
                }
            }
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleGetCurrentMode(sys::Message *req) -> sys::MessagePointer
{
    if (nullptr != dynamic_cast<settings::Messages::GetCurrentMode *>(req)) {
        if (modeChangeRecipients.end() != modeChangeRecipients.find(req->sender)) {
            auto msgMode = std::make_shared<settings::Messages::CurrentModeChanged>(dbGetCurrentMode());
            parentService->bus.sendUnicast(std::move(msgMode), req->sender);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleAddMode(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<settings::Messages::AddMode *>(req)) {
        if (dbAddMode(msg->getProfileName())) {
            auto allModes = dbGetAllModes();
            for (const auto &service : modeChangeRecipients) {
                if (service != msg->sender) {
                    auto msgAllModes = std::make_shared<settings::Messages::ModeListResponse>(allModes);
                    parentService->bus.sendUnicast(std::move(msgAllModes), service);
                }
            }
        }
    }
    return std::make_shared<sys::ResponseMessage>();
}
auto SettingsAgent::handleListModes(sys::Message *req) -> sys::MessagePointer
{
    if (nullptr != dynamic_cast<settings::Messages::ListModes *>(req)) {
        modeChangeRecipients.insert(req->sender);
        auto msgAllModes = std::make_shared<settings::Messages::ModeListResponse>(dbGetAllModes());
        parentService->bus.sendUnicast(std::move(msgAllModes), req->sender);
    }
    return std::make_shared<sys::ResponseMessage>();
}

M module-services/service-db/agents/settings/SettingsAgent.hpp => module-services/service-db/agents/settings/SettingsAgent.hpp +0 -30
@@ 51,20 51,6 @@ class SettingsAgent : public DatabaseAgent
    auto dbRegisterValueChange(const settings::EntryPath &path) -> bool;
    auto dbUnregisterValueChange(const settings::EntryPath &path) -> bool;

    auto dbRegisterOnProfileChange(const std::string &service) -> bool;
    auto dbUnregisterOnProfileChange(const std::string &service) -> bool;
    auto dbSetCurrentProfile(const std::string &profile) -> bool;
    auto dbGetCurrentProfile() -> std::string;
    auto dbGetAllProfiles() -> std::list<std::string>;
    auto dbAddProfile(const std::string &profile) -> bool;

    auto dbRegisterOnModeChange(const std::string &service) -> bool;
    auto dbUnregisterOnModeChange(const std::string &service) -> bool;
    auto dbSetCurrentMode(const std::string &mode) -> bool;
    auto dbGetCurrentMode() -> std::string;
    auto dbGetAllModes() -> std::list<std::string>;
    auto dbAddMode(const std::string &mode) -> bool;

    auto getDbInitString() -> const std::string override;

    // msg handlers


@@ 73,20 59,4 @@ class SettingsAgent : public DatabaseAgent
    auto handleSetVariable(sys::Message *req) -> sys::MessagePointer;
    auto handleRegisterOnVariableChange(sys::Message *req) -> sys::MessagePointer;
    auto handleUnregisterOnVariableChange(sys::Message *req) -> sys::MessagePointer;

    // profile
    auto handleRegisterProfileChange(sys::Message *req) -> sys::MessagePointer;
    auto handleUnregisterProfileChange(sys::Message *req) -> sys::MessagePointer;
    auto handleSetCurrentProfile(sys::Message *req) -> sys::MessagePointer;
    auto handleGetCurrentProfile(sys::Message *req) -> sys::MessagePointer;
    auto handleAddProfile(sys::Message *req) -> sys::MessagePointer;
    auto handleListProfiles(sys::Message *req) -> sys::MessagePointer;

    // mode
    auto handleRegisterOnModeChange(sys::Message *req) -> sys::MessagePointer;
    auto handleUnregisterOnModeChange(sys::Message *req) -> sys::MessagePointer;
    auto handleSetCurrentMode(sys::Message *req) -> sys::MessagePointer;
    auto handleGetCurrentMode(sys::Message *req) -> sys::MessagePointer;
    auto handleAddMode(sys::Message *req) -> sys::MessagePointer;
    auto handleListModes(sys::Message *req) -> sys::MessagePointer;
};

M module-services/service-db/agents/settings/SettingsCache.cpp => module-services/service-db/agents/settings/SettingsCache.cpp +43 -11
@@ 1,29 1,61 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-db/SettingsCache.hpp>
#include <mutex.hpp>

namespace settings
{

    namespace
    {
        class SettingsCacheImpl : public SettingsCache
        {
          public:
            static SettingsCacheImpl &get()
            {
                static SettingsCacheImpl instance;
                return instance;
            }

            const std::string &getValue(const EntryPath &path) const;
            void setValue(const EntryPath &path, const std::string &value);

          private:
            std::map<EntryPath, std::string> settingsMap;
            mutable cpp_freertos::MutexStandard settingsMutex;
        };

        const std::string &SettingsCacheImpl::getValue(const EntryPath &path) const
        {
            static const std::string empty;
            cpp_freertos::LockGuard lock(settingsMutex);
            auto pathIt = settingsMap.find(path);
            if (settingsMap.end() != pathIt) {
                return pathIt->second;
            }
            return empty;
        }

        void SettingsCacheImpl::setValue(const EntryPath &path, const std::string &value)
        {
            cpp_freertos::LockGuard lock(settingsMutex);
            settingsMap[path] = value;
        }
    } // namespace

    SettingsCache *SettingsCache::getInstance()
    {
        static SettingsCache instance;
        return &instance;
        return &SettingsCacheImpl::get();
    }

    const std::string &SettingsCache::getValue(const EntryPath &path) const
    {
        static const std::string empty = "";
        cpp_freertos::LockGuard lock(settingsMutex);
        auto pathIt = settingsMap.find(path);
        if (settingsMap.end() != pathIt) {
            return pathIt->second;
        }
        return empty;
        return SettingsCacheImpl::get().getValue(path);
    }

    void SettingsCache::setValue(const EntryPath &path, const std::string &value)
    {
        cpp_freertos::LockGuard lock(settingsMutex);
        settingsMap[path] = value;
        return SettingsCacheImpl::get().setValue(path, value);
    }
} // namespace settings

A module-services/service-db/agents/settings/SettingsProxy.cpp => module-services/service-db/agents/settings/SettingsProxy.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 <service-db/EntryPath.hpp>
#include <service-db/SettingsMessages.hpp>
#include <service-db/Settings.hpp>
#include <service-db/SettingsCache.hpp>
#include <service-db/DBServiceName.hpp>
#include <Service/ServiceProxy.hpp>
#include <Service/Service.hpp>
#include <utility>

namespace settings
{

    SettingsProxy::SettingsProxy(const service::ServiceProxy &interface) : service::ServiceProxy(interface)
    {}

    SettingsProxy::~SettingsProxy()
    {
        deinit();
    }

    std::string SettingsProxy::ownerName() noexcept
    {
        return getService()->GetName();
    }

    void SettingsProxy::init(std::function<void(EntryPath, std::string)> onChangeHandler)
    {
        this->onChangeHandler = std::move(onChangeHandler);

        getService()->bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
        getService()->connect(typeid(settings::Messages::VariableChanged), [this](sys::Message *req) {
            if (auto msg = dynamic_cast<settings::Messages::VariableChanged *>(req)) {
                onChange(msg->getPath(), msg->getValue().value_or(""));
            }
            return std::make_shared<sys::ResponseMessage>();
        });
    }

    void SettingsProxy::deinit()
    {
        if (isValid()) {
            getService()->disconnect(typeid(settings::Messages::VariableChanged));
        }
    }

    [[nodiscard]] bool SettingsProxy::isValid() const noexcept
    {
        return service::ServiceProxy::isValid();
    }

    void SettingsProxy::onChange(EntryPath path, std::string value)
    {
        if (onChangeHandler) {
            onChangeHandler(std::move(path), std::move(value));
        }
    }

    template <typename T, typename... Args> auto message(sys::BusProxy &bus, Args... args)
    {
        bus.sendUnicast(std::make_shared<T>(args...), service::name::db);
    }

    void SettingsProxy::registerValueChange(EntryPath path)
    {
        message<settings::Messages::RegisterOnVariableChange>(getService()->bus, std::move(path));
    }

    void SettingsProxy::unregisterValueChange(EntryPath path)
    {
        message<settings::Messages::UnregisterOnVariableChange>(getService()->bus, std::move(path));
    }

    void SettingsProxy::setValue(const EntryPath &path, const std::string &value)
    {
        message<settings::Messages::SetVariable>(getService()->bus, path, value);
    }

} // namespace settings

A module-services/service-db/service-db/EntryPath.hpp => module-services/service-db/service-db/EntryPath.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 <service-db/SettingsScope.hpp>
#include <string>
#include <type_traits>

namespace settings
{
    struct EntryPath
    {
        std::string mode = "";
        std::string service;
        std::string profile = "";
        std::string variable;
        SettingsScope scope;

        [[nodiscard]] auto to_string(std::string sep = "\\") const -> std::string
        {
            if (SettingsScope::Global == scope) {
                return variable;
            }
            return mode + sep + service + sep + profile + sep + variable;
        }

        void parse(const std::string &dbPath);
    };
} // namespace settings

M module-services/service-db/service-db/Settings.hpp => module-services/service-db/service-db/Settings.hpp +15 -42
@@ 1,12 1,11 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 <module-sys/Service/Message.hpp>
#include <service-db/DBServiceName.hpp>
#include "EntryPath.hpp"
#include "SettingsScope.hpp"
#include "SettingsMessages.hpp"
#include "SettingsProxy.hpp"

#include <cstdint>
#include <functional>


@@ 15,23 14,23 @@
#include <memory>
#include <optional>
#include <string>
#include <exception>

namespace settings
{
    class SettingsCache;

    class Settings
    {
      public:
        using ValueChangedCallback           = std::function<void(const std::string &)>;
        using ValueChangedCallbackWithName   = std::function<void(const std::string &, const std::string &value)>;
        using ProfileChangedCallback         = std::function<void(const std::string &)>;
        using ModeChangedCallback            = ProfileChangedCallback;
        using ListOfProfiles                 = std::list<std::string>;
        using ListOfModes                    = ListOfProfiles;
        using OnAllProfilesRetrievedCallback = std::function<void(const ListOfProfiles &)>;
        using OnAllModesRetrievedCallback    = std::function<void(const ListOfModes &)>;

        Settings(sys::Service *app, const std::string &dbAgentName = service::name::db, SettingsCache *cache = nullptr);
        Settings() = default;
        virtual ~Settings();

        void init(const service::ServiceProxy &interface);
        void deinit();

        void setValue(const std::string &variableName,
                      const std::string &variableValue,


@@ 44,43 43,17 @@ namespace settings
                                 SettingsScope scope = SettingsScope::AppLocal);
        void unregisterValueChange(const std::string &variableName, SettingsScope scope = SettingsScope::AppLocal);
        /// unregisters all registered variables (both global and local)
        void unregisterValueChange();
        std::string getValue(const std::string &variableName, SettingsScope scope = SettingsScope::AppLocal);

        void getAllProfiles(OnAllProfilesRetrievedCallback cb);
        void setCurrentProfile(const std::string &profile);
        void addProfile(const std::string &profile);
        void registerProfileChange(ProfileChangedCallback);
        void unregisterProfileChange();

        void getAllModes(OnAllModesRetrievedCallback cb);
        void setCurrentMode(const std::string &mode);
        void addMode(const std::string &mode);
        void registerModeChange(ModeChangedCallback);
        void unregisterModeChange();
        SettingsCache *getCache();

      private:
        std::string dbAgentName;

        std::shared_ptr<sys::Service> app;
        std::string serviceName;
        std::string phoneMode;
        std::string profile;

        SettingsCache *cache;
        SettingsProxy interface;

        using ValueCb = std::map<EntryPath, std::pair<ValueChangedCallback, ValueChangedCallbackWithName>>;

        ValueCb cbValues;
        ModeChangedCallback cbMode;
        OnAllModesRetrievedCallback cbAllModes;
        ProfileChangedCallback cbProfile;
        OnAllProfilesRetrievedCallback cbAllProfiles;
        void sendMsg(std::shared_ptr<settings::Messages::SettingsMessage> &&msg);
        void registerHandlers();
        auto handleVariableChanged(sys::Message *req) -> sys::MessagePointer;
        auto handleCurrentProfileChanged(sys::Message *req) -> sys::MessagePointer;
        auto handleCurrentModeChanged(sys::Message *req) -> sys::MessagePointer;
        auto handleProfileListResponse(sys::Message *req) -> sys::MessagePointer;
        auto handleModeListResponse(sys::Message *req) -> sys::MessagePointer;

        void handleVariableChanged(const EntryPath &path, const std::string &value);
    };
} // namespace settings

M module-services/service-db/service-db/SettingsCache.hpp => module-services/service-db/service-db/SettingsCache.hpp +3 -5
@@ 1,7 1,8 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <mutex.hpp>
#pragma once

#include "SettingsMessages.hpp"
#include <map>



@@ 13,9 14,6 @@ namespace settings
        const std::string &getValue(const EntryPath &path) const;
        void setValue(const EntryPath &path, const std::string &value);
        static SettingsCache *getInstance();

      private:
        std::map<EntryPath, std::string> settingsMap;
        mutable cpp_freertos::MutexStandard settingsMutex;
        virtual ~SettingsCache() = default;
    };
} // namespace settings

M module-services/service-db/service-db/SettingsMessages.hpp => module-services/service-db/service-db/SettingsMessages.hpp +7 -225
@@ 5,49 5,16 @@

#include <MessageType.hpp>
#include <Service/Message.hpp>
#include <service-db/SettingsScope.hpp>
#include "EntryPath.hpp"

#include <list>
#include <memory>
#include <set>
#include <utility>
#include <variant>
#include <module-utils/Utils.hpp>

namespace settings
{
    struct EntryPath
    {
        std::string mode;
        std::string service;
        std::string profile;
        std::string variable;
        SettingsScope scope;

        [[nodiscard]] auto to_string(std::string sep = "\\") const -> std::string
        {
            if (SettingsScope::Global == scope) {
                return variable;
            }
            return mode + sep + service + sep + profile + sep + variable;
        }

        void parse(const std::string &dbPath)
        {
            auto parts = utils::split(dbPath, "\\", false);
            if (1 == parts.size()) {
                variable = dbPath;
                scope    = SettingsScope::Global;
            }
            else {
                mode     = parts[0];
                service  = parts[1];
                profile  = parts[2];
                variable = parts[3];
                scope    = SettingsScope::AppLocal;
            }
        }
    };

    bool operator<(const EntryPath &lhs, const EntryPath &rhs) noexcept;



@@ 66,7 33,7 @@ namespace settings
          public:
            Variable() = default;
            explicit Variable(EntryPath path, std::optional<std::string> value = {})
                : SettingsMessage(), path(std::move(path)), value(std::move(value))
                : path(std::move(path)), value(std::move(value))
            {}

            [[nodiscard]] auto getValue() const -> std::optional<std::string>


@@ 88,7 55,7 @@ namespace settings
        {
          public:
            GetVariable() = default;
            explicit GetVariable(EntryPath path) : Variable(path)
            explicit GetVariable(EntryPath path) : Variable(std::move(path))
            {}
        };



@@ 96,7 63,7 @@ namespace settings
        {
          public:
            SetVariable() = default;
            SetVariable(EntryPath path, std::string value) : Variable(path, value)
            SetVariable(EntryPath path, std::string value) : Variable(std::move(path), value)
            {}
        };



@@ 104,7 71,7 @@ namespace settings
        {
          public:
            RegisterOnVariableChange() = default;
            explicit RegisterOnVariableChange(EntryPath path) : Variable(path)
            explicit RegisterOnVariableChange(EntryPath path) : Variable(std::move(path))
            {}
        };



@@ 112,7 79,7 @@ namespace settings
        {
          public:
            UnregisterOnVariableChange() = default;
            explicit UnregisterOnVariableChange(EntryPath path) : Variable(path)
            explicit UnregisterOnVariableChange(EntryPath path) : Variable(std::move(path))
            {}
        };



@@ 121,7 88,7 @@ namespace settings
          public:
            VariableChanged() = default;
            explicit VariableChanged(EntryPath path, std::string value, std::string old_value)
                : Variable(path, value), old_value(std::move(old_value))
                : Variable(std::move(path), value), old_value(std::move(old_value))
            {}

            [[nodiscard]] auto getOldValue() const -> std::string


@@ 133,142 100,6 @@ namespace settings
            std::string old_value;
        };

        /// Profiles manipulation
        class ListProfiles : public SettingsMessage
        {
          public:
            [[nodiscard]] auto getProfiles() const -> std::set<std::string>
            {
                return profiles;
            }

          private:
            std::set<std::string> profiles;
        };

        class ProfileSettingsMessage : public SettingsMessage
        {
          public:
            [[nodiscard]] auto getProfileName() const -> std::string
            {
                return profile;
            }

          protected:
            ProfileSettingsMessage() = default;
            explicit ProfileSettingsMessage(std::string name) : SettingsMessage(), profile(std::move(name))
            {}

          protected:
            std::string profile;
        };

        class SetCurrentProfile : public ProfileSettingsMessage
        {
          public:
            SetCurrentProfile() = default;
            explicit SetCurrentProfile(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        class AddProfile : public ProfileSettingsMessage
        {
          public:
            AddProfile() = default;
            explicit AddProfile(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        class GetCurrentProfile : public ProfileSettingsMessage
        {
          public:
            GetCurrentProfile() = default;
            explicit GetCurrentProfile(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        class RegisterOnProfileChange : public SettingsMessage
        {};

        class UnregisterOnProfileChange : public SettingsMessage
        {};

        class CurrentProfileChanged : public ProfileSettingsMessage
        {
          public:
            CurrentProfileChanged() = default;
            explicit CurrentProfileChanged(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        /// Modes manipulation
        class ListModes : public SettingsMessage
        {
          public:
            [[nodiscard]] auto getModes() const -> std::set<std::string>
            {
                return modes;
            }

          private:
            std::set<std::string> modes;
        };

        class Mode : public SettingsMessage
        {
          public:
            [[nodiscard]] auto getModeName() const -> std::string
            {
                return mode;
            }

          protected:
            Mode() = default;
            explicit Mode(std::string mode) : SettingsMessage(), mode(std::move(mode))
            {}

          protected:
            std::string mode;
        };

        class SetCurrentMode : public ProfileSettingsMessage
        {
          public:
            SetCurrentMode() = default;
            explicit SetCurrentMode(std::string mode) : ProfileSettingsMessage(mode)
            {}
        };

        class AddMode : public ProfileSettingsMessage
        {
          public:
            AddMode() = default;
            explicit AddMode(std::string mode) : ProfileSettingsMessage(mode)
            {}
        };

        class GetCurrentMode : public ProfileSettingsMessage
        {
          public:
            GetCurrentMode() = default;
            explicit GetCurrentMode(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        class RegisterOnModeChange : public SettingsMessage
        {};

        class UnregisterOnModeChange : public SettingsMessage
        {};

        class CurrentModeChanged : public ProfileSettingsMessage
        {
          public:
            CurrentModeChanged() = default;
            explicit CurrentModeChanged(std::string profile) : ProfileSettingsMessage(profile)
            {}
        };

        class ValueResponse : sys::ResponseMessage
        {
          public:


@@ 308,54 139,5 @@ namespace settings
            EntryPath path;
            std::optional<std::string> value;
        };

        class ProfileResponse : public ValueResponse
        {
          public:
            ProfileResponse() = default;
            explicit ProfileResponse(std::string profile) : ValueResponse(profile)
            {}
        };

        class ModeResponse : public ValueResponse
        {
          public:
            ModeResponse() = default;
            explicit ModeResponse(std::string mode) : ValueResponse(mode)
            {}
        };

        class ListResponse : public SettingsMessage
        {
          public:
            ListResponse() = default;
            explicit ListResponse(std::list<std::string> value) : SettingsMessage(), value(std::move(value))
            {}

            [[nodiscard]] auto getValue() const -> std::list<std::string>
            {
                return value;
            }

          protected:
            std::list<std::string> value;
        };

        class ProfileListResponse : public ListResponse
        {
          public:
            ProfileListResponse() = default;
            explicit ProfileListResponse(std::list<std::string> profiles) : ListResponse(profiles)
            {}
        };

        class ModeListResponse : public ListResponse
        {
          public:
            ModeListResponse() = default;
            explicit ModeListResponse(std::list<std::string> modes) : ListResponse(modes)
            {}
        };

    } // namespace Messages
} // namespace settings

A module-services/service-db/service-db/SettingsProxy.hpp => module-services/service-db/service-db/SettingsProxy.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 "EntryPath.hpp"
#include "Service/ServiceProxy.hpp"
#include <functional>

namespace settings
{
    class SettingsProxy : private service::ServiceProxy
    {
      public:
        SettingsProxy() = default;
        ~SettingsProxy();
        explicit SettingsProxy(const service::ServiceProxy &interface);
        void init(std::function<void(EntryPath, std::string)> onChangeHandler);
        void deinit();
        [[nodiscard]] bool isValid() const noexcept;

        void onChange(EntryPath path, std::string value);
        void registerValueChange(EntryPath path);
        void unregisterValueChange(EntryPath path);
        void setValue(const EntryPath &path, const std::string &value);
        [[nodiscard]] std::string ownerName() noexcept;

      private:
        std::function<void(EntryPath, std::string)> onChangeHandler;
    };
} // namespace settings

M module-services/service-db/test/CMakeLists.txt => module-services/service-db/test/CMakeLists.txt +1 -0
@@ 32,3 32,4 @@ add_catch2_executable(
)

add_subdirectory(test-settings)
add_subdirectory(test-settings-Settings)

M module-services/service-db/test/test-service-db-settings-messages.cpp => module-services/service-db/test/test-service-db-settings-messages.cpp +1 -47
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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>           // for Section, SourceLineInfo, SECTION, SectionInfo, StringRef, TEST_CASE


@@ 83,24 83,6 @@ namespace settings
            }
            return std::make_shared<sys::ResponseMessage>();
        };

        sys::MessagePointer handleListProfiles(sys::Message *req)
        {
            if (dynamic_cast<settings::Messages::ListProfiles *>(req) != nullptr) {
                std::list<std::string> profiles = {"silent", "loud"};
                return std::make_shared<settings::Messages::ProfileListResponse>(profiles);
            }
            return std::make_shared<sys::ResponseMessage>();
        };

        sys::MessagePointer handleListModes(sys::Message *req)
        {
            if (dynamic_cast<settings::Messages::ListProfiles *>(req) != nullptr) {
                std::list<std::string> modes = {"mode1", "mode2"};
                return std::make_shared<settings::Messages::ModeListResponse>(modes);
            }
            return std::make_shared<sys::ResponseMessage>();
        };
    };
} // namespace settings



@@ 125,32 107,4 @@ TEST_CASE("Settings Messages")
                                     {"mode", "service", "profile", "variable", settings::SettingsScope::AppLocal})),
                                 "db-worker");
    }

    SECTION("Send profile messages")
    {
        settings::Service settings("settings");
        settings.InitHandler();

        settings.bus.sendUnicast(std::make_shared<settings::Messages::ListProfiles>(), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::AddProfile>("new-profile"), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::GetCurrentProfile>(), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::CurrentProfileChanged>("profile"), "settings");
    }

    SECTION("Send mode messages")
    {
        settings::Service settings("settings");
        settings.InitHandler();

        settings.bus.sendUnicast(std::make_shared<settings::Messages::ListModes>(), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::GetCurrentMode>(), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::AddMode>("new-mode"), "settings");

        settings.bus.sendUnicast(std::make_shared<settings::Messages::CurrentModeChanged>("mode"), "settings");
    }
}

A module-services/service-db/test/test-settings-Settings/CMakeLists.txt => module-services/service-db/test/test-settings-Settings/CMakeLists.txt +17 -0
@@ 0,0 1,17 @@
# service-db tests
add_catch2_executable(
        NAME
            settings-Settings
        SRCS
            main.cpp
            test-settings-Interface.cpp
            test-settings-Settings.cpp
            ../../agents/settings/Settings.cpp
            ../../EntryPath.cpp
        INCLUDE
            ${CMAKE_SOURCE_DIR}/module-sys
            ${CMAKE_SOURCE_DIR}/source
            ${CMAKE_SOURCE_DIR}/module-services/service-db
            ${CMAKE_SOURCE_DIR}/module-utils/
        DEPS
)

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

#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>

A module-services/service-db/test/test-settings-Settings/test-settings-Interface.cpp => module-services/service-db/test/test-settings-Settings/test-settings-Interface.cpp +72 -0
@@ 0,0 1,72 @@
// 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 <memory>
#include <module-sys/Service/ServiceProxy.hpp>

namespace sys
{
    // mockup
    class Service
    {};

} // namespace sys

class Interface_imp : public service::ServiceProxy
{
  public:
    using ServiceProxy::getService;
    Interface_imp() = default;
    explicit Interface_imp(std::weak_ptr<sys::Service> s) : service::ServiceProxy(std::move(s))
    {}
};

TEST_CASE("Interface - not initialized")
{
    SECTION("base api")
    {
        auto interface = service::ServiceProxy();
        REQUIRE(!interface.isValid());
    }

    SECTION("inherited api")
    {
        auto interface = Interface_imp();
        REQUIRE_THROWS_AS(interface.getService(), std::runtime_error);
    }
}

TEST_CASE("Interface - initialized")
{
    auto service   = std::make_shared<sys::Service>();
    auto interface = Interface_imp(service);
    REQUIRE(interface.isValid());
    REQUIRE_NOTHROW(interface.getService());
}

TEST_CASE("Interface - expired")
{
    Interface_imp *interface;
    std::shared_ptr<sys::Service> expired;
    {
        auto service = std::make_shared<sys::Service>();
        interface    = new Interface_imp(service);
    }
    REQUIRE(!interface->isValid());
    REQUIRE_THROWS_AS(interface->getService(), std::runtime_error);
    delete interface;
}

TEST_CASE("Interface - copied")
{
    auto service    = std::make_shared<sys::Service>();
    auto interface  = Interface_imp(service);
    auto interface2 = interface;

    REQUIRE(interface.isValid());
    REQUIRE_NOTHROW(interface.getService());
    REQUIRE(interface2.isValid());
    REQUIRE_NOTHROW(interface2.getService());
    REQUIRE(interface2.getService() == interface.getService());
}

A module-services/service-db/test/test-settings-Settings/test-settings-Settings.cpp => module-services/service-db/test/test-settings-Settings/test-settings-Settings.cpp +91 -0
@@ 0,0 1,91 @@
// 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 <module-services/service-db/service-db/Settings.hpp>
#include "module-services/service-db/service-db/SettingsCache.hpp"
#include <utility>

/// stub
namespace settings
{
    SettingsProxy::~SettingsProxy()
    {}
    SettingsProxy::SettingsProxy(const service::ServiceProxy &interface) : service::ServiceProxy(interface)
    {}
    void SettingsProxy::init(std::function<void(EntryPath, std::string)> onChangeHandler)
    {
        this->onChangeHandler = std::move(onChangeHandler);
    }
    void SettingsProxy::deinit()
    {}

    void SettingsProxy::onChange(EntryPath path, std::string value)
    {
        if (onChangeHandler) {
            onChangeHandler(std::move(path), std::move(value));
        }
    };
    bool SettingsProxy::isValid() const noexcept
    {
        return true;
    }
    void SettingsProxy::registerValueChange(EntryPath){};
    void SettingsProxy::unregisterValueChange(EntryPath){};
    void SettingsProxy::setValue(const EntryPath &path, const std::string &value){};
    std::string SettingsProxy::ownerName() noexcept
    {
        return "";
    }

    const std::string &SettingsCache::getValue(const EntryPath &path) const
    {
        static const std::string val;
        return val;
    }
    void SettingsCache::setValue(const EntryPath &path, const std::string &value)
    {}

    SettingsCache *SettingsCache::getInstance()
    {
        static SettingsCache s;
        return &s;
    }

} // namespace settings

/// TODO shall we warn here... on uninitialized settings?
TEST_CASE("Settings - not initialized")
{
    settings::Settings setting;
    SECTION("Not initialized settings")
    {
        auto val = setting.getValue("lol");
        REQUIRE(val.empty());
    }

    SECTION("dead initialized settings")
    {
        auto val = setting.getValue("lol");
        REQUIRE(val.empty());
    }
}

TEST_CASE("Settings - initialized")
{
    /// this will require stubbing
    SECTION("get Value - not exists")
    {}

    SECTION("get Value - exists")
    {}

    SECTION("get Value - different type that expected")
    {}

    SECTION("set value - no value")
    {}

    SECTION("set value - override")
    {}
}

M module-services/service-db/test/test-settings/CMakeLists.txt => module-services/service-db/test/test-settings/CMakeLists.txt +0 -2
@@ 11,7 11,5 @@ add_catch2_executable(
            module-audio
            service-cellular
            module-cellular
            iosyscalls
        DEPS
            disk_image
)

M module-services/service-db/test/test-settings/test-service-db-settings-api.cpp => module-services/service-db/test/test-settings/test-service-db-settings-api.cpp +52 -97
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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>


@@ 27,105 27,60 @@ TEST_CASE("SettingsApi")
        std::shared_ptr<settings::MyService> varWritter;
        std::shared_ptr<settings::MyService> varReader;
        std::shared_ptr<settings::AppTest> testVar;
        std::shared_ptr<settings::ServiceProfile> profWritter;
        std::shared_ptr<settings::ServiceProfile> profReader;
        std::shared_ptr<settings::AppTestProfileMode> testProf;
        std::shared_ptr<settings::ServiceMode> modeWritter;
        std::shared_ptr<settings::ServiceMode> modeReader;
        std::shared_ptr<settings::AppTestProfileMode> testMode;
        std::shared_ptr<std::mutex> testStart;

        manager->StartSystem(
            nullptr,
            [manager,
             &varWritter,
             &varReader,
             &testVar,
             &profWritter,
             &profReader,
             &testProf,
             &modeWritter,
             &modeReader,
             &testMode,
             &testStart]() {
                // preliminary
                testStart = std::make_shared<std::mutex>();
                testStart->lock();
                std::cout << "start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
                auto ret = sys::SystemManager::RunSystemService(
                    std::make_shared<EventManager>(service::name::evt_manager), manager.get());
                ret &= sys::SystemManager::RunSystemService(std::make_shared<ServiceDB>(), manager.get());

                varWritter = std::make_shared<settings::MyService>("writterVar");
                varReader  = std::make_shared<settings::MyService>("readerVar");

                ret &= sys::SystemManager::RunSystemService(varWritter, manager.get());
                ret &= sys::SystemManager::RunSystemService(varReader, manager.get());

                testVar = std::make_shared<settings::AppTest>("appTest", varWritter, varReader, testStart);
                ret &= sys::SystemManager::RunSystemService(testVar, manager.get());

                profWritter = std::make_shared<settings::ServiceProfile>("writterProf");
                profReader  = std::make_shared<settings::ServiceProfile>("readerProf");

                ret &= sys::SystemManager::RunSystemService(profWritter, manager.get());
                ret &= sys::SystemManager::RunSystemService(profReader, manager.get());

                testProf = std::make_shared<settings::AppTestProfileMode>(
                    "appTestProfile", profWritter, profReader, testStart);
                ret &= sys::SystemManager::RunSystemService(testProf, manager.get());

                modeWritter = std::make_shared<settings::ServiceMode>("writterMode");
                modeReader  = std::make_shared<settings::ServiceMode>("readerMode");

                ret &= sys::SystemManager::RunSystemService(modeWritter, manager.get());
                ret &= sys::SystemManager::RunSystemService(modeReader, manager.get());

                testMode =
                    std::make_shared<settings::AppTestProfileMode>("appTestMode", modeWritter, modeReader, testStart);
                ret &= sys::SystemManager::RunSystemService(testMode, manager.get());

                std::cout << "koniec start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
                testStart->unlock();
                auto msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTest");

                msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTestProfile");

                msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
                manager->bus.sendUnicast(std::move(msgStart), "appTestMode");

                return ret;
            });

        // start application
        cpp_freertos::Thread::StartScheduler();

        // check the results
        std::cout << "testVar values:" << std::endl << std::flush;
        for (auto s : testVar->v) {
            std::cout << s << std::endl << std::flush;
        }
        REQUIRE(testVar->v.size() == 3);
        REQUIRE(testVar->v[1] == testVar->v[0] + "1");
        REQUIRE(testVar->v[2] == testVar->v[1] + "2");

        // check the result
        std::cout << "testProf values:" << std::endl << std::flush;
        for (auto s : testProf->v) {
            std::cout << s << std::endl << std::flush;
        }
        REQUIRE(testProf->v[1] == testProf->v[0] + "1");
        REQUIRE(testProf->v[2] == testProf->v[0] + "12");
        REQUIRE(testProf->v[3] == "other");
        std::shared_ptr<settings::Settings> postMortemSetting;

        manager->StartSystem(nullptr, [manager, &varWritter, &varReader, &testVar, &testStart, &postMortemSetting]() {
            // preliminary
            testStart = std::make_shared<std::mutex>();
            testStart->lock();
            std::cout << "start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            auto ret = sys::SystemManager::RunSystemService(std::make_shared<EventManager>(service::name::evt_manager),
                                                            manager.get());
            ret &= sys::SystemManager::RunSystemService(std::make_shared<ServiceDB>(), manager.get());

            varWritter = std::make_shared<settings::MyService>("writterVar");
            varReader  = std::make_shared<settings::MyService>("readerVar");

            postMortemSetting = varWritter->getSettings();

            ret &= sys::SystemManager::RunSystemService(varWritter, manager.get());
            ret &= sys::SystemManager::RunSystemService(varReader, manager.get());

            testVar = std::make_shared<settings::AppTest>("appTest", varWritter, varReader, testStart);
            ret &= sys::SystemManager::RunSystemService(testVar, manager.get());

        std::cout << "testMode values:" << std::endl << std::flush;
        for (auto s : testMode->v) {
            std::cout << s << std::endl << std::flush;
            std::cout << "koniec start thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            testStart->unlock();
            auto msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTest");

            msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTestProfile");

            msgStart = std::make_shared<settings::UTMsg::UTMsgStart>();
            manager->bus.sendUnicast(std::move(msgStart), "appTestMode");

            return ret;
        });

        try {
            // start application
            cpp_freertos::Thread::StartScheduler();

            // check the results
            std::cout << "testVar values:" << std::endl << std::flush;
            for (const auto &s : testVar->v) {
                std::cout << s << std::endl << std::flush;
            }
            REQUIRE(testVar->v.size() == 3);
            REQUIRE(testVar->v[1] == testVar->v[0] + "1");
            REQUIRE(testVar->v[2] == testVar->v[1] + "2");
        }
        catch (std::exception &error) {
            std::cout << error.what() << std::endl;
            exit(1);
        }
        REQUIRE(testMode->v[1] == testMode->v[0] + "1");
        REQUIRE(testMode->v[2] == testMode->v[0] + "12");
        REQUIRE(testMode->v[3] == "other");
    }
}

M module-services/service-db/test/test-settings/test-service-db-settings-testapps.hpp => module-services/service-db/test/test-settings/test-service-db-settings-testapps.hpp +52 -37
@@ 66,6 66,21 @@ namespace settings
        std::string last_v;
        std::vector<std::string> v;

        void setState(State state)
        {
            printf("state change: [%s]->[%s]\n",
                   std::string(magic_enum::enum_name(this->state)).c_str(),
                   std::string(magic_enum::enum_name(state)).c_str());
            this->state = state;
        }
        bool isState(State cmp)
        {
            printf("state compare: [%s]->[%s]\n",
                   std::string(magic_enum::enum_name(this->state)).c_str(),
                   std::string(magic_enum::enum_name(state)).c_str());
            return this->state == cmp;
        }

        AppTest(std::string name,
                std::shared_ptr<MyService> setter,
                std::shared_ptr<MyService> getter,


@@ 75,7 90,7 @@ namespace settings
        {}
        sys::ReturnCodes InitHandler() override
        {
            state = State::Unk;
            setState(State::Unk);
            return sys::ReturnCodes::Success;
        }
        sys::ReturnCodes DeinitHandler() override


@@ 88,48 103,48 @@ namespace settings
            if (nullptr != dynamic_cast<settings::UTMsg::UTMsgStart *>(msg)) {
                testStart->lock();
                testStart->unlock();
                if (state != State::Unk) {
                if (!isState(State::Unk)) {
                    closeSystem();
                }
                else {
                    state    = State::Start;
                    setState(State::Start);
                    auto msg = std::make_shared<settings::UTMsg::ReqRegValChg>("brightness", "none");
                    bus.sendUnicast(std::move(msg), getter->GetName());
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfRegValChg *>(msg)) {
                if (state == State::Start) {
                    state = State::Register;
                if (isState(State::Start)) {
                    setState(State::Register);
                }
            }
            else if (auto m = dynamic_cast<settings::UTMsg::CnfValChg *>(msg)) {
                if (state == State::Register) {
                    state = State::RegisterStartVal;
                if (isState(State::Register)) {
                    setState(State::RegisterStartVal);
                    v.push_back(m->value);
                    auto msg = std::make_shared<settings::UTMsg::ReqSetVal>("brightness", v[0] + "1");
                    bus.sendUnicast(std::move(msg), setter->GetName());
                }
                else if (state == State::RegisterSetVal) {
                else if (isState(State::RegisterSetVal)) {
                    if (m->value == v[0] + "1") {
                        v.push_back(m->value);
                        auto msg = std::make_shared<settings::UTMsg::ReqUnRegValChg>("brightness", "empty");
                        bus.sendUnicast(std::move(msg), getter->GetName());
                        state = State::UnregisterWait;
                        setState(State::UnregisterWait);
                    }
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfUnRegValChg *>(msg)) {
                if (state == State::UnregisterWait) {
                    state    = State::Unregister;
                if (isState(State::UnregisterWait)) {
                    setState(State::Unregister);
                    auto msg = std::make_shared<settings::UTMsg::ReqSetVal>("brightness", v.back() + "2");
                    bus.sendUnicast(std::move(msg), setter->GetName());
                }
            }
            else if (auto m = dynamic_cast<settings::UTMsg::CnfReqSetVal *>(msg)) {
                if (state == State::RegisterStartVal) {
                    state = State::RegisterSetVal;
                if (isState(State::RegisterStartVal)) {
                    setState(State::RegisterSetVal);
                }
                else if (state == State::Unregister) {
                else if (isState(State::Unregister)) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(200));
                    v.push_back(m->value);
                    auto msg = std::make_shared<settings::UTMsg::UTMsgStop>();


@@ 137,7 152,7 @@ namespace settings
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::UTMsgStop *>(msg)) {
                if (state == State::Unregister) {
                if (isState(State::Unregister)) {
                    closeSystem();
                }
            }


@@ 168,69 183,69 @@ namespace settings
            if (nullptr != dynamic_cast<settings::UTMsg::UTMsgStart *>(msg)) {
                testStart->lock();
                testStart->unlock();
                if (state != State::Unk) {
                if (!isState(State::Unk)) {
                    closeSystem();
                }
                else {
                    state    = State::Start;
                    setState(State::Start);
                    auto msg = std::make_shared<settings::UTMsg::ReqRegProfileChg>();
                    bus.sendUnicast(std::move(msg), getter->GetName());
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfRegProfileChg *>(msg)) {
                if (state == State::Start) {
                    state = State::Register;
                if (isState(State::Start)) {
                    setState(State::Register);
                }
            }
            else if (auto m = dynamic_cast<settings::UTMsg::ProfileChg *>(msg)) {
                if (state == State::Register) {
                    state = State::RegisterStartVal;
                if (isState(State::Register)) {
                    setState(State::RegisterStartVal);
                    v.push_back(m->name);
                    auto msg = std::make_shared<settings::UTMsg::ReqSetCurrentProfile>(m->name + "1");
                    bus.sendUnicast(std::move(msg), setter->GetName());
                }
                else if (state == State::RegisterSetVal) {
                else if (isState(State::RegisterSetVal)) {
                    if (m->name == v[0] + "1") {
                        v.push_back(m->name);
                        auto msg = std::make_shared<settings::UTMsg::ReqUnRegProfileChg>();
                        bus.sendUnicast(std::move(msg), getter->GetName());
                        state = State::UnregisterWait;
                        setState(State::UnregisterWait);
                    }
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfUnRegProfileChg *>(msg)) {
                if (state == State::UnregisterWait) {
                    state    = State::Unregister;
                if (isState(State::UnregisterWait)) {
                    setState(State::Unregister);
                    auto msg = std::make_shared<settings::UTMsg::ReqSetCurrentProfile>(v.back() + "2");
                    bus.sendUnicast(std::move(msg), setter->GetName());
                }
            }
            else if (auto m = dynamic_cast<settings::UTMsg::CnfSetCurrentProfile *>(msg)) {
                if (state == State::RegisterStartVal) {
                    state = State::RegisterSetVal;
                if (isState(State::RegisterStartVal)) {
                    setState(State::RegisterSetVal);
                }
                else if (state == State::Unregister) {
                else if (isState(State::Unregister)) {
                    v.push_back(m->name);
                    auto msg = std::make_shared<settings::UTMsg::ReqGetAllProfiles>();
                    bus.sendUnicast(std::move(msg), getter->GetName());
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfGetAllProfiles *>(msg)) {
                if (state == State::Unregister) {
                    state = State::RegisterAllWait;
                if (isState(State::Unregister)) {
                    setState(State::RegisterAllWait);
                }
            }
            else if (auto m = dynamic_cast<settings::UTMsg::ProfilesChg *>(msg)) {
                if (state == State::RegisterAllWait) {
                    state = State::RegisterAll;
                if (isState(State::RegisterAllWait)) {
                    setState(State::RegisterAll);
                    for (auto prof : m->profiles) {
                        v.push_back(prof);
                    }
                    auto msg = std::make_shared<settings::UTMsg::ReqAddProfile>("other");
                    bus.sendUnicast(std::move(msg), setter->GetName());
                }
                else if (state == State::RegisterAllAddWait) {
                    state = State::RegisterAllAdd;
                else if (isState(State::RegisterAllAddWait)) {
                    setState(State::RegisterAllAdd);
                    for (auto prof : m->profiles) {
                        v.push_back(prof);
                    }


@@ 240,12 255,12 @@ namespace settings
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::CnfAddProfile *>(msg)) {
                if (state == State::RegisterAll) {
                    state = State::RegisterAllAddWait;
                if (isState(State::RegisterAll)) {
                    setState(State::RegisterAllAddWait);
                }
            }
            else if (nullptr != dynamic_cast<settings::UTMsg::UTMsgStop *>(msg)) {
                if (state == State::RegisterAllAdd) {
                if (isState(State::RegisterAllAdd)) {
                    closeSystem();
                }
            }

M module-services/service-db/test/test-settings/test-service-db-settings-testservices.hpp => module-services/service-db/test/test-settings/test-service-db-settings-testservices.hpp +10 -109
@@ 1,6 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <memory>

namespace settings
{
    class MyService : public sys::Service


@@ 8,7 10,6 @@ namespace settings
      public:
        MyService(const std::string &name) : sys::Service(name)
        {
            mySettings = std::make_shared<settings::Settings>(this);
        }
        std::shared_ptr<settings::Settings> mySettings;
        std::vector<std::string> valChanged;


@@ 59,11 60,15 @@ namespace settings
        }
        sys::ReturnCodes InitHandler() override
        {
            std::cout << "inithandler thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            std::cout << "InitHandler thr_id: " << std::this_thread::get_id() << "name: " << GetName() << std::endl
                      << std::flush;
            mySettings = std::make_shared<settings::Settings>();
            mySettings->init(service::ServiceProxy(shared_from_this()));
            return sys::ReturnCodes::Success;
        }
        sys::ReturnCodes DeinitHandler() override
        {
            mySettings->deinit();
            std::cout << "deinithandler thr_id: " << std::this_thread::get_id() << std::endl << std::flush;
            return sys::ReturnCodes::Success;
        }


@@ 71,115 76,11 @@ namespace settings
        {
            return sys::ReturnCodes::Success;
        }
    };

    class ServiceProfile : public MyService
    {
      public:
        ServiceProfile(const std::string &name) : MyService(name)
        {}
        settings::Settings::ListOfProfiles profiles;
        std::string profile;
        sys::MessagePointer DataReceivedHandler(sys::DataMessage *req, sys::ResponseMessage *resp) override
        std::shared_ptr<settings::Settings> getSettings()
        {
            if (auto msg = dynamic_cast<settings::UTMsg::ReqRegProfileChg *>(req)) {
                debug("ReqRegProfileChg", msg->name, msg->value);
                whoRequestedNotifyOnChange = msg->sender;
                mySettings->registerProfileChange(([this](const std::string &profile) {
                    this->profile = profile;
                    auto cnf      = std::make_shared<settings::UTMsg::ProfileChg>(profile);
                    bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
                }));
                auto cnf = std::make_shared<settings::UTMsg::CnfRegProfileChg>();
                bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqUnRegProfileChg *>(req)) {
                // unregister
                debug("ReqUnRegProfileChg", msg->name, msg->value);
                mySettings->unregisterProfileChange();
                auto cnf = std::make_shared<settings::UTMsg::CnfUnRegProfileChg>();
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqSetCurrentProfile *>(req)) {
                // set value
                debug("ReqSetCurrentProfile", msg->name, msg->value);
                mySettings->setCurrentProfile(msg->name);
                auto cnf = std::make_shared<settings::UTMsg::CnfSetCurrentProfile>(msg->name);
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqGetAllProfiles *>(req)) {
                debug("ReqGetAllProfiles", msg->name, msg->value);
                mySettings->getAllProfiles(([this](const settings::Settings::ListOfProfiles &profiles) {
                    this->profiles = profiles;
                    auto cnf       = std::make_shared<settings::UTMsg::ProfilesChg>(profiles);
                    bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
                }));
                auto cnf = std::make_shared<settings::UTMsg::CnfGetAllProfiles>();
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqAddProfile *>(req)) {
                debug("ReqAddProfile", msg->name, msg->value);
                mySettings->addProfile(msg->name);
                auto cnf = std::make_shared<settings::UTMsg::CnfAddProfile>(msg->name);
                bus.sendUnicast(std::move(cnf), msg->sender);
            }

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

    class ServiceMode : public MyService
    {
      public:
        ServiceMode(const std::string &name) : MyService(name)
        {}
        settings::Settings::ListOfModes modes;
        std::string mode;
        sys::MessagePointer DataReceivedHandler(sys::DataMessage *req, sys::ResponseMessage *resp) override
        {
            if (auto msg = dynamic_cast<settings::UTMsg::ReqRegProfileChg *>(req)) {
                debug("ReqRegProfileChg", msg->name, msg->value);
                whoRequestedNotifyOnChange = msg->sender;
                mySettings->registerModeChange(([this](const std::string &mode) {
                    this->mode = mode;
                    auto cnf   = std::make_shared<settings::UTMsg::ProfileChg>(mode);
                    bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
                }));
                auto cnf = std::make_shared<settings::UTMsg::CnfRegProfileChg>();
                bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqUnRegProfileChg *>(req)) {
                // unregister
                debug("ReqUnRegProfileChg", msg->name, msg->value);
                mySettings->unregisterModeChange();
                auto cnf = std::make_shared<settings::UTMsg::CnfUnRegProfileChg>();
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqSetCurrentProfile *>(req)) {
                // set value
                debug("ReqSetCurrentProfile", msg->name, msg->value);
                mySettings->setCurrentMode(msg->name);
                auto cnf = std::make_shared<settings::UTMsg::CnfSetCurrentProfile>(msg->name);
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqGetAllProfiles *>(req)) {
                debug("ReqGetAllProfiles", msg->name, msg->value);
                mySettings->getAllModes(([this](const settings::Settings::ListOfModes &modes) {
                    this->modes = modes;
                    auto cnf    = std::make_shared<settings::UTMsg::ProfilesChg>(modes);
                    bus.sendUnicast(std::move(cnf), whoRequestedNotifyOnChange);
                }));
                auto cnf = std::make_shared<settings::UTMsg::CnfGetAllProfiles>();
                bus.sendUnicast(std::move(cnf), msg->sender);
            }
            else if (auto msg = dynamic_cast<settings::UTMsg::ReqAddProfile *>(req)) {
                debug("ReqAddProfile", msg->name, msg->value);
                mySettings->addMode(msg->name);
                auto cnf = std::make_shared<settings::UTMsg::CnfAddProfile>(msg->name);
                bus.sendUnicast(std::move(cnf), msg->sender);
            }

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

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +5 -2
@@ 68,8 68,6 @@ ServiceDesktop::ServiceDesktop()
    bus.channels.push_back(sys::BusChannel::PhoneLockChanges);

    updateOS         = std::make_unique<UpdateMuditaOS>(this);
    settings         = std::make_unique<settings::Settings>(this);
    usbSecurityModel = std::make_unique<sdesktop::USBSecurityModel>(this, settings.get());
}

ServiceDesktop::~ServiceDesktop()


@@ 79,6 77,10 @@ ServiceDesktop::~ServiceDesktop()

sys::ReturnCodes ServiceDesktop::InitHandler()
{

    settings = std::make_unique<settings::Settings>();
    settings->init(service::ServiceProxy(shared_from_this()));
    usbSecurityModel = std::make_unique<sdesktop::USBSecurityModel>(this, settings.get());
    desktopWorker = std::make_unique<WorkerDesktop>(this, *usbSecurityModel.get());
    const bool ret =
        desktopWorker->init({{sdesktop::RECEIVE_QUEUE_BUFFER_NAME, sizeof(std::string *), sdesktop::cdc_queue_len},


@@ 295,6 297,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()

sys::ReturnCodes ServiceDesktop::DeinitHandler()
{
    settings->deinit();
    desktopWorker->deinit();
    return sys::ReturnCodes::Success;
}

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +1 -0
@@ 2,6 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DeveloperModeHelper.hpp"
#include "service-db/SettingsMessages.hpp"
#include <service-desktop/DesktopMessages.hpp>
#include <parser/ParserUtils.hpp>


M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +9 -7
@@ 55,13 55,11 @@ namespace
} // namespace

EventManager::EventManager(const std::string &name)
    : sys::Service(name, "", stackDepth),
      settings(std::make_shared<settings::Settings>(this)), loggerTimer{sys::TimerFactory::createPeriodicTimer(
                                                                this,
                                                                loggerTimerName,
                                                                std::chrono::milliseconds{loggerDelayMs},
                                                                [this](sys::Timer & /*timer*/) { dumpLogsToFile(); })},
      screenLightControl(std::make_unique<screen_light_control::ScreenLightControl>(settings, this)),
    : sys::Service(name, "", stackDepth), loggerTimer{sys::TimerFactory::createPeriodicTimer(
                                              this,
                                              loggerTimerName,
                                              std::chrono::milliseconds{loggerDelayMs},
                                              [this](sys::Timer & /*timer*/) { dumpLogsToFile(); })},
      Vibra(std::make_unique<vibra_handle::Vibra>(this))
{
    LOG_INFO("[%s] Initializing", name.c_str());


@@ 209,6 207,8 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
// Invoked during initialization
sys::ReturnCodes EventManager::InitHandler()
{
    settings->init(service::ServiceProxy(shared_from_this()));
    screenLightControl = std::make_unique<screen_light_control::ScreenLightControl>(settings, this);

    connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) {
        using namespace sdesktop::developerMode;


@@ 329,6 329,8 @@ sys::ReturnCodes EventManager::InitHandler()

sys::ReturnCodes EventManager::DeinitHandler()
{
    settings->deinit();

    EventWorker->close();
    EventWorker.reset();
    EventWorker = nullptr;

M module-services/service-evtmgr/service-evtmgr/EventManager.hpp => module-services/service-evtmgr/service-evtmgr/EventManager.hpp +1 -1
@@ 38,7 38,7 @@ class EventManager : public sys::Service
    void toggleTorchOnOff();
    void toggleTorchColor();

    std::shared_ptr<settings::Settings> settings;
    std::shared_ptr<settings::Settings> settings = std::make_shared<settings::Settings>();
    sys::TimerHandle loggerTimer;
    sys::TimerHandle keypadLightTimer;
    bsp::keypad_backlight::State keypadLightState{bsp::keypad_backlight::State::off};

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +0 -1
@@ 3,7 3,6 @@

#pragma once

#include "FreeRTOSConfig.h"
#include "SystemReturnCodes.hpp"

namespace sys

M module-sys/Service/Service.hpp => module-sys/Service/Service.hpp +1 -13
@@ 3,6 3,7 @@

#pragma once

#include "ServiceForward.hpp"
#include "BusProxy.hpp"
#include "Common.hpp"  // for ReturnCodes, ServicePriority, BusChannels
#include "Mailbox.hpp" // for Mailbox


@@ 25,19 26,6 @@

namespace sys
{
    struct Proxy;
    class Timer;

    namespace timer
    {
        class SystemTimer;
    } // namespace timer
} // namespace sys

namespace sys
{
    using MessageHandler = std::function<MessagePointer(Message *)>;

    class Service : public cpp_freertos::Thread, public std::enable_shared_from_this<Service>
    {
      public:

A module-sys/Service/ServiceForward.hpp => module-sys/Service/ServiceForward.hpp +20 -0
@@ 0,0 1,20 @@
// 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 "MessageForward.hpp"
#include <functional>

namespace sys
{
    using MessageHandler = std::function<MessagePointer(Message *)>;

    struct Proxy;
    class Timer;

    namespace timer
    {
        class SystemTimer;
    } // namespace timer
} // namespace sys

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

#pragma once

#include <memory>
#include <stdexcept>
#include <utility>

namespace sys
{
    class Service;
}

namespace service
{
    /// Proxy class for classes using Service raw pointer
    /// - initialized in InitHandler
    /// - provides weak bounding with Service (to avoid infinite shared ptr loop)
    /// - used to provide Class <-> System interface
    /// - invalid ServiceProxy will throw runtime error instead of crash on invalid Service* ptr
    class ServiceProxy
    {
      private:
        std::weak_ptr<sys::Service> service;

      protected:
        [[nodiscard]] std::shared_ptr<sys::Service> getService()
        {
            if (auto val = service.lock(); val != nullptr) {
                return val;
            }
            throw std::runtime_error("no service");
        }

      public:
        explicit ServiceProxy(std::weak_ptr<sys::Service> service) : service(std::move(service))
        {}
        ServiceProxy()          = default;
        virtual ~ServiceProxy() = default;
        [[nodiscard]] bool isValid() const noexcept
        {
            return !service.expired();
        }
    };
} // namespace service

A module-utils/Split.hpp => module-utils/Split.hpp +51 -0
@@ 0,0 1,51 @@
// 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 <vector>
#include <sstream>

namespace utils
{
    template <typename Out> void split(const std::string &s, char delim, Out result)
    {
        std::stringstream ss(s);
        std::string item;
        while (std::getline(ss, item, delim)) {
            *(result++) = item;
        }
    }

    static inline std::vector<std::string> split(const std::string &s, char delim)
    {
        std::vector<std::string> elems;
        split(s, delim, std::back_inserter(elems));
        return elems;
    }

    static inline std::vector<std::string> split(const std::string &s,
                                                 const std::string &delimiter,
                                                 const bool skipEmptyTokens = true)
    {
        size_t pos_start = 0, pos_end, delim_len = delimiter.length();
        std::string token;
        std::vector<std::string> res;
        uint32_t tokenCount = 0;

        while (((pos_end = s.find(delimiter, pos_start)) != std::string::npos)) {
            token     = s.substr(pos_start, pos_end - pos_start);
            pos_start = pos_end + delim_len;
            if (!skipEmptyTokens || !token.empty()) {
                tokenCount++;
                res.push_back(token);
            }
        }

        token = s.substr(pos_start);
        if (!skipEmptyTokens || !token.empty()) {
            res.push_back(token);
        }
        return res;
    }
} // namespace utils

M module-utils/Utils.hpp => module-utils/Utils.hpp +2 -41
@@ 1,7 1,8 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "Split.hpp"
#include "i18n/i18n.hpp"
#include <algorithm> // std::find_if_not
#include <sstream>


@@ 29,46 30,6 @@ namespace utils
        s << std::setw(sizeof(T) * 2) << std::hex << static_cast<unsigned long long>(c);
        return s.str();
    }
    template <typename Out> void split(const std::string &s, char delim, Out result)
    {
        std::stringstream ss(s);
        std::string item;
        while (std::getline(ss, item, delim)) {
            *(result++) = item;
        }
    }

    static inline std::vector<std::string> split(const std::string &s, char delim)
    {
        std::vector<std::string> elems;
        split(s, delim, std::back_inserter(elems));
        return elems;
    }

    static inline std::vector<std::string> split(const std::string &s,
                                                 const std::string &delimiter,
                                                 const bool skipEmptyTokens = true)
    {
        size_t pos_start = 0, pos_end, delim_len = delimiter.length();
        std::string token;
        std::vector<std::string> res;
        uint32_t tokenCount = 0;

        while (((pos_end = s.find(delimiter, pos_start)) != std::string::npos)) {
            token     = s.substr(pos_start, pos_end - pos_start);
            pos_start = pos_end + delim_len;
            if (!skipEmptyTokens || !token.empty()) {
                tokenCount++;
                res.push_back(token);
            }
        }

        token = s.substr(pos_start);
        if (!skipEmptyTokens || !token.empty()) {
            res.push_back(token);
        }
        return res;
    }

    static inline std::string removeNewLines(const std::string &s)
    {