~aleteoryx/muditaos

b9661350f8dd5180b5f8fe7c758e5ae16452f7cf — Piotr Tański 5 years ago 619ad21
[EGD-5697] Framework for phone modes introduced

It allows to transmit phone modes information to listening services.
M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +9 -1
@@ 107,8 107,10 @@ namespace app::manager
          blockingTimer{std::make_unique<sys::Timer>(
              timerBlock, this, std::numeric_limits<sys::ms>::max(), sys::Timer::Type::SingleShot)},
          shutdownDelay{std::make_unique<sys::Timer>(timerShutdownDelay, this, shutdown_delay_ms)},
          settings(std::make_unique<settings::Settings>(this))
          settings(std::make_unique<settings::Settings>(this)),
          phoneModeObserver(std::make_unique<sys::phone_modes::Observer>())
    {
        bus.channels.push_back(sys::BusChannel::PhoneModeChanges);
        registerMessageHandlers();
        blockingTimer->connect([this](sys::Timer &) { onPhoneLocked(); });
    }


@@ 195,6 197,12 @@ namespace app::manager

    void ApplicationManager::registerMessageHandlers()
    {
        phoneModeObserver->connect(this);
        phoneModeObserver->subscribe(
            [](sys::phone_modes::PhoneMode phoneMode, sys::phone_modes::Tethering tetheringMode) {
                LOG_INFO("Phone mode changed.");
            });

        connect(typeid(ApplicationStatusRequest), [this](sys::Message *request) {
            auto msg = static_cast<ApplicationStatusRequest *>(request);
            return std::make_shared<ApplicationStatusResponse>(msg->checkAppName,

M module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/service-appmgr/model/ApplicationManager.hpp +2 -0
@@ 14,6 14,7 @@
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <Service/Timer.hpp>
#include <PhoneModes/Observer.hpp>
#include <SwitchData.hpp>

#include <deque>


@@ 154,6 155,7 @@ namespace app::manager
        std::tuple<ApplicationName, actions::ActionId, actions::ActionParamsPtr> pendingAction;

        std::unique_ptr<settings::Settings> settings;
        std::unique_ptr<sys::phone_modes::Observer> phoneModeObserver;
        void displayLanguageChanged(std::string value);
        void lockTimeChanged(std::string value);
        void inputLanguageChanged(std::string value);

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +11 -3
@@ 40,6 40,7 @@
#include <module-apps/messages/AppMessage.hpp>
#include <SystemManager/messages/CpuFrequencyMessage.hpp>
#include <common_data/EventStore.hpp>
#include <SystemManager/messages/PhoneModeRequest.hpp>

EventManager::EventManager(const std::string &name)
    : sys::Service(name), screenLightControl(std::make_unique<screen_light_control::ScreenLightControl>(this))


@@ 102,9 103,16 @@ sys::MessagePointer EventManager::DataReceivedHandler(sys::DataMessage *msgl, sy
        auto message = std::make_shared<sevm::KbdMessage>();
        message->key = msg->key;

        if (message->key.state == RawKey::State::Pressed && message->key.key_code == bsp::KeyCodes::FnRight) {
            // and state == ShutDown
            bus.sendUnicast(message, service::name::system_manager);
        if (message->key.state == RawKey::State::Pressed) {
            const auto code = message->key.key_code;
            if (code == bsp::KeyCodes::FnRight) {
                bus.sendUnicast(message, service::name::system_manager);
            }
            else if (code == bsp::KeyCodes::SSwitchUp || code == bsp::KeyCodes::SSwitchMid ||
                     code == bsp::KeyCodes::SSwitchDown) {
                const auto mode = sys::SystemManager::translateSliderState(message->key);
                bus.sendUnicast(std::make_shared<sys::PhoneModeRequest>(mode), service::name::system_manager);
            }
        }

        // send key to focused application

M module-sys/CMakeLists.txt => module-sys/CMakeLists.txt +2 -0
@@ 14,6 14,8 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Service.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/Timer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Service/CpuSentinel.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/PhoneModes/Subject.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/PhoneModes/Observer.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/SystemManager.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/DependencyGraph.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/SystemManager/PowerManager.cpp

A module-sys/PhoneModes/Common.hpp => module-sys/PhoneModes/Common.hpp +49 -0
@@ 0,0 1,49 @@
// 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/Message.hpp>

namespace sys::phone_modes
{
    enum class PhoneMode
    {
        Connected,
        DoNotDisturb,
        Offline
    };

    enum class Tethering
    {
        Off,
        On
    };

    class PhoneModeChanged : public DataMessage
    {
      public:
        PhoneModeChanged(PhoneMode phoneMode, Tethering tetheringMode)
            : DataMessage{MessageType::MessageTypeUninitialized}, phoneMode{phoneMode}, tethering{tetheringMode}
        {}

        [[nodiscard]] auto getPhoneMode() const noexcept
        {
            return phoneMode;
        }
        [[nodiscard]] auto getTetheringMode() const noexcept
        {
            return tethering;
        }

      private:
        PhoneMode phoneMode;
        Tethering tethering;
    };

    class PhoneModeChangedSuccessfully : public ResponseMessage
    {};

    class PhoneModeChangeFailed : public ResponseMessage
    {};
} // namespace sys::phone_modes

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

#include "Observer.hpp"

#include <Service/Service.hpp>

namespace sys::phone_modes
{
    void Observer::connect(Service *owner)
    {
        owner->connect(typeid(PhoneModeChanged), [this](sys::Message *request) -> sys::MessagePointer {
            return handlePhoneModeChange(static_cast<PhoneModeChanged *>(request));
        });
    }

    void Observer::subscribe(OnChangeCallback &&onChange,
                             OnCompleteCallback &&onComplete,
                             OnErrorCallback &&onError) noexcept
    {
        onChangeCallback   = std::move(onChange);
        onCompleteCallback = std::move(onComplete);
        onErrorCallback    = std::move(onError);
    }

    bool Observer::isInMode(PhoneMode mode) const noexcept
    {
        return phoneMode == mode;
    }

    bool Observer::isTetheringOn() const noexcept
    {
        return tetheringMode == Tethering::On;
    }

    sys::MessagePointer Observer::handlePhoneModeChange(PhoneModeChanged *message)
    {
        phoneMode     = message->getPhoneMode();
        tetheringMode = message->getTetheringMode();

        try {
            onPhoneModeChanged();
        }
        catch (const std::exception &) {
            return std::make_shared<PhoneModeChangeFailed>();
        }
        return std::make_shared<PhoneModeChangedSuccessfully>();
    }

    void Observer::onPhoneModeChanged()
    {
        if (!onChangeCallback) {
            LOG_WARN("No subscriber on phone mode change.");
            return;
        }

        try {
            onChangeCallback(phoneMode, tetheringMode);
            if (onCompleteCallback) {
                onCompleteCallback();
            }
        }
        catch (const std::exception &error) {
            if (onErrorCallback) {
                onErrorCallback(error);
            }
            throw;
        }
    }
} // namespace sys::phone_modes

A module-sys/PhoneModes/Observer.hpp => module-sys/PhoneModes/Observer.hpp +43 -0
@@ 0,0 1,43 @@
// 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 <functional>
#include <stdexcept>

#include "Common.hpp"

namespace sys
{
    class Service; // Forward declaration
} // namespace sys

namespace sys::phone_modes
{
    class Observer
    {
      public:
        using OnChangeCallback   = std::function<void(PhoneMode, Tethering)>;
        using OnCompleteCallback = std::function<void()>;
        using OnErrorCallback    = std::function<void(const std::exception &)>;

        void connect(Service *owner);
        void subscribe(OnChangeCallback &&onChange,
                       OnCompleteCallback &&onComplete = {},
                       OnErrorCallback &&onError       = {}) noexcept;

        bool isInMode(PhoneMode mode) const noexcept;
        bool isTetheringOn() const noexcept;

      private:
        sys::MessagePointer handlePhoneModeChange(PhoneModeChanged *message);
        void onPhoneModeChanged();

        OnChangeCallback onChangeCallback;
        OnCompleteCallback onCompleteCallback;
        OnErrorCallback onErrorCallback;
        PhoneMode phoneMode     = PhoneMode::Connected;
        Tethering tetheringMode = Tethering::Off;
    };
} // namespace sys::phone_modes

A module-sys/PhoneModes/Subject.cpp => module-sys/PhoneModes/Subject.cpp +61 -0
@@ 0,0 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 "Subject.hpp"

#include <Service/Service.hpp>

#include <module-utils/magic_enum/include/magic_enum.hpp>

#include <stdexcept>
#include <utility>

namespace sys::phone_modes
{
    Subject::Subject(Service *owner) : owner{owner}
    {
        if (owner == nullptr) {
            throw std::invalid_argument{"Subject's owner is invalid"};
        }
    }

    void Subject::setMode(PhoneMode _phoneMode, Tethering _tetheringMode)
    {
        if (const bool changed = changePhoneMode(_phoneMode) || changeTetheringMode(_tetheringMode); changed) {
            notifyChange();
        }
    }

    void Subject::setPhoneMode(PhoneMode mode)
    {
        if (const auto changed = changePhoneMode(mode); changed) {
            notifyChange();
        }
    }

    bool Subject::changePhoneMode(PhoneMode mode) noexcept
    {
        return std::exchange(phoneMode, mode) != mode;
    }

    void Subject::setTetheringMode(Tethering mode)
    {
        if (const auto changed = changeTetheringMode(mode); changed) {
            notifyChange();
        }
    }

    bool Subject::changeTetheringMode(Tethering mode) noexcept
    {
        return std::exchange(tetheringMode, mode) != mode;
    }

    void Subject::notifyChange()
    {
        LOG_INFO("Phone modes changed: Phone mode: [%s]; Tethering: [%s]. Notifying phone modes observers.",
                 magic_enum::enum_name(phoneMode).data(),
                 magic_enum::enum_name(tetheringMode).data());
        auto message = std::make_shared<PhoneModeChanged>(phoneMode, tetheringMode);
        owner->bus.sendMulticast(std::move(message), BusChannel::PhoneModeChanges);
    }
} // namespace sys::phone_modes

A module-sys/PhoneModes/Subject.hpp => module-sys/PhoneModes/Subject.hpp +33 -0
@@ 0,0 1,33 @@
// 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 "Common.hpp"

namespace sys
{
    class Service; // Forward declaration
} // namespace sys

namespace sys::phone_modes
{
    class Subject
    {
      public:
        explicit Subject(Service *owner);

        void setMode(PhoneMode _phoneMode, Tethering _tetheringMode);
        void setPhoneMode(PhoneMode mode);
        void setTetheringMode(Tethering mode);

      private:
        void notifyChange();
        bool changePhoneMode(PhoneMode mode) noexcept;
        bool changeTetheringMode(Tethering mode) noexcept;

        Service *owner;
        PhoneMode phoneMode     = PhoneMode::Connected;
        Tethering tetheringMode = Tethering::Off;
    };
} // namespace sys::phone_modes

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +4 -1
@@ 21,7 21,8 @@ namespace sys
        ServiceFotaNotifications,
        AntennaNotifications,
        ServiceEvtmgrNotifications,
        CalendarNotifications
        CalendarNotifications,
        PhoneModeChanges,
    };

    enum class ServicePriority


@@ 111,6 112,8 @@ inline const char *c_str(sys::BusChannel channel)
        return "ServiceEvtmgrNotifications";
    case sys::BusChannel::CalendarNotifications:
        return "CalendarNotifications";
    case sys::BusChannel::PhoneModeChanges:
        return "PhoneModeChanges";
    }
    return "";
}

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +31 -0
@@ 25,12 25,20 @@
#include "messages/DeviceRegistrationMessage.hpp"
#include "messages/SentinelRegistrationMessage.hpp"
#include "messages/RequestCpuFrequencyMessage.hpp"
#include "messages/PhoneModeRequest.hpp"
#include <time/ScopedTime.hpp>

const inline size_t systemManagerStack = 4096 * 2;

namespace sys
{
    namespace
    {
        const std::map<bsp::KeyCodes, phone_modes::PhoneMode> SliderStateToPhoneModeMapping = {
            {bsp::KeyCodes::SSwitchUp, phone_modes::PhoneMode::Connected},
            {bsp::KeyCodes::SSwitchMid, phone_modes::PhoneMode::DoNotDisturb},
            {bsp::KeyCodes::SSwitchDown, phone_modes::PhoneMode::Offline}};
    } // namespace

    using namespace cpp_freertos;
    using namespace std;


@@ 146,6 154,7 @@ namespace sys
        powerManager  = std::make_unique<PowerManager>();
        cpuStatistics = std::make_unique<CpuStatistics>();
        deviceManager = std::make_unique<DeviceManager>();
        phoneModeSubject = std::make_unique<phone_modes::Subject>(this);

        systemInit = std::move(sysInit);
        userInit   = std::move(appSpaceInit);


@@ 370,6 379,11 @@ namespace sys
            return sys::MessageNone{};
        });

        connect(typeid(PhoneModeRequest), [this](sys::Message *message) -> sys::MessagePointer {
            auto request = static_cast<PhoneModeRequest *>(message);
            return handlePhoneModeRequest(request);
        });

        deviceManager->RegisterNewDevice(powerManager->getExternalRamDevice());

        return ReturnCodes::Success;


@@ 476,6 490,23 @@ namespace sys
        powerManager->UpdateCpuFrequency(cpuStatistics->GetPercentageCpuLoad());
    }

    phone_modes::PhoneMode SystemManager::translateSliderState(const RawKey &key)
    {
        const auto code = key.key_code;
        if (code != bsp::KeyCodes::SSwitchUp && code != bsp::KeyCodes::SSwitchMid &&
            code != bsp::KeyCodes::SSwitchDown) {
            throw std::invalid_argument{"Invalid key code passed."};
        }
        return SliderStateToPhoneModeMapping.at(code);
    }

    MessagePointer SystemManager::handlePhoneModeRequest(PhoneModeRequest *request)
    {
        LOG_INFO("Phone mode change requested.");
        phoneModeSubject->setPhoneMode(request->getPhoneMode());
        return MessageNone{};
    }

    std::vector<std::shared_ptr<Service>> SystemManager::servicesList;
    cpp_freertos::MutexStandard SystemManager::destroyMutex;
    std::unique_ptr<PowerManager> SystemManager::powerManager;

M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +12 -0
@@ 19,6 19,8 @@
#include "Service/Service.hpp"
#include "Service/ServiceCreator.hpp"
#include "PowerManager.hpp"
#include "PhoneModes/Subject.hpp"
#include <common_data/RawKey.hpp>
#include "Constants.hpp"
#include "CpuStatistics.hpp"
#include "DeviceManager.hpp"


@@ 34,6 36,8 @@ namespace sys
        inline constexpr std::chrono::milliseconds timerPeriodInterval{100ms};
    } // namespace constants

    class PhoneModeRequest; // Forward declaration

    enum class Code
    {
        CloseSystem,


@@ 92,6 96,11 @@ namespace sys
        /// @note there is no fallback
        static bool DestroyService(const std::string &name, Service *caller, TickType_t timeout = 5000);

        /// Translates a slider state into a phone mode.
        /// \param key  Slider button state
        /// \return Phone mode.
        static phone_modes::PhoneMode translateSliderState(const RawKey &key);

        /// Kill service
        /// @note - this is final, it straight takes service, calls it's close callback and it's gone
        /// please mind that services & apps not registered in SystemManager cant be killed - these should be handled by


@@ 143,10 152,13 @@ namespace sys
        /// periodic update of cpu statistics
        void CpuStatisticsTimerHandler();

        MessagePointer handlePhoneModeRequest(PhoneModeRequest *request);

        bool cpuStatisticsTimerInit{false};

        std::vector<std::unique_ptr<BaseServiceCreator>> systemServiceCreators;
        std::unique_ptr<sys::Timer> cpuStatisticsTimer;
        std::unique_ptr<phone_modes::Subject> phoneModeSubject;
        InitFunction userInit;
        InitFunction systemInit;


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

#pragma once

#include <module-sys/PhoneModes/Common.hpp>

namespace sys
{
    class PhoneModeRequest : public sys::DataMessage
    {
      public:
        explicit PhoneModeRequest(phone_modes::PhoneMode mode)
            : sys::DataMessage(MessageType::MessageTypeUninitialized), mode{mode}
        {}

        [[nodiscard]] auto getPhoneMode() const noexcept -> phone_modes::PhoneMode
        {
            return mode;
        }

      private:
        phone_modes::PhoneMode mode;
    };
} // namespace sys