~aleteoryx/muditaos

bd06eacbda91c0e71e7d175642c616055f625cd2 — Marek Niepieklo 4 years ago 64cfe81
[CP-403] Replace old update code

Removed update related code
Removed update test scripts
51 files changed, 71 insertions(+), 2793 deletions(-)

M module-apps/application-desktop/ApplicationDesktop.cpp
M module-apps/application-desktop/CMakeLists.txt
M module-apps/application-desktop/data/DesktopData.hpp
M module-apps/application-desktop/include/application-desktop/ApplicationDesktop.hpp
M module-apps/application-desktop/windows/DesktopMainWindow.cpp
M module-apps/application-desktop/windows/DesktopMainWindow.hpp
D module-apps/application-desktop/windows/PostUpdateWindow.cpp
D module-apps/application-desktop/windows/PostUpdateWindow.hpp
D module-apps/application-desktop/windows/Update.cpp
D module-apps/application-desktop/windows/Update.hpp
D module-apps/application-desktop/windows/UpdateProgress.cpp
D module-apps/application-desktop/windows/UpdateProgress.hpp
M module-bsp/bsp/usb/usb.hpp
M module-services/service-appmgr/CMakeLists.txt
M module-services/service-appmgr/include/service-appmgr/messages/Message.hpp
D module-services/service-appmgr/include/service-appmgr/messages/SetOsUpdateVersion.hpp
D module-services/service-appmgr/include/service-appmgr/messages/UpdateInProgress.hpp
M module-services/service-appmgr/include/service-appmgr/model/ApplicationManager.hpp
D module-services/service-appmgr/messages/SetOsUpdateVersion.cpp
D module-services/service-appmgr/messages/UpdateInProgress.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-desktop/CMakeLists.txt
M module-services/service-desktop/ServiceDesktop.cpp
M module-services/service-desktop/WorkerDesktop.cpp
M module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.cpp
M module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.hpp
M module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp
M module-services/service-desktop/endpoints/filesystem/FileOperations.hpp
M module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp
M module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp
M module-services/service-desktop/endpoints/update/UpdateEndpoint.cpp
M module-services/service-desktop/endpoints/update/UpdateEndpoint.hpp
R module-services/service-desktop/endpoints/{developerMode/Mode => update}/UpdateHelper.cpp
R module-services/service-desktop/endpoints/{developerMode/Mode => update}/UpdateHelper.hpp
D module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp
D module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp
D module-services/service-desktop/endpoints/update/UpdateOSTypes.hpp
M module-services/service-desktop/service-desktop/DesktopMessages.hpp
M module-services/service-desktop/service-desktop/ServiceDesktop.hpp
M module-services/service-desktop/service-desktop/WorkerDesktop.hpp
M module-services/service-desktop/tests/test-fs-ep-operations.cpp
M module-services/service-evtmgr/EventManager.cpp
M module-sys/SystemManager/SystemManager.cpp
M module-sys/SystemManager/SystemManager.hpp
D test/firmware_update_test/.gdbinit-1051-autocontinue
D test/firmware_update_test/README.md
D test/firmware_update_test/update.py
M test/harness
D test/pytest/service-desktop/test_update.py
M test/pytest/test_updater.py
M third-party/usb_stack
M module-apps/application-desktop/ApplicationDesktop.cpp => module-apps/application-desktop/ApplicationDesktop.cpp +0 -95
@@ 11,10 11,7 @@
#include "MmiInternalMsgWindow.hpp"
#include "MmiPullWindow.hpp"
#include "MmiPushWindow.hpp"
#include "PostUpdateWindow.hpp"
#include "Reboot.hpp"
#include "Update.hpp"
#include "UpdateProgress.hpp"

#include <apps-common/messages/AppMessage.hpp>
#include <AppWindow.hpp>


@@ 95,7 92,6 @@ namespace app
    // Invoked upon receiving data message
    sys::MessagePointer ApplicationDesktop::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {

        auto retMsg = Application::DataReceivedHandler(msgl);
        // if message was handled by application's template there is no need to process further.
        if (dynamic_cast<sys::ResponseMessage *>(retMsg.get())->retCode == sys::ReturnCodes::Success) {


@@ 103,9 99,6 @@ namespace app
        }

        bool handled = false;
        if (auto msg = dynamic_cast<sdesktop::UpdateOsMessage *>(msgl)) {
            handled = handle(msg);
        }

        // handle database response
        if (resp != nullptr) {


@@ 129,18 122,6 @@ namespace app
        }
    }

    auto ApplicationDesktop::handle(sdesktop::UpdateOsMessage *msg) -> bool
    {
        if (msg != nullptr && msg->messageType == updateos::UpdateMessageType::UpdateFoundOnBoot) {

            if (msg->updateStats.updateFile.has_filename()) {
                LOG_DEBUG("handle pending update found: %s", msg->updateStats.updateFile.c_str());
            }
        }

        return true;
    }

    void ApplicationDesktop::handleNotificationsChanged(std::unique_ptr<gui::SwitchData> notificationsParams)
    {
        if (auto window = getCurrentWindow()->getName();


@@ 160,7 141,6 @@ namespace app
    // Invoked during initialization
    sys::ReturnCodes ApplicationDesktop::InitHandler()
    {

        auto ret = Application::InitHandler();
        if (ret != sys::ReturnCodes::Success) {
            return ret;


@@ 168,50 148,12 @@ namespace app

        createUserInterface();

        connect(sdesktop::UpdateOsMessage(), [&](sys::Message *msg) {
            auto *updateMsg = dynamic_cast<sdesktop::UpdateOsMessage *>(msg);
            if (updateMsg != nullptr && updateMsg->messageType == updateos::UpdateMessageType::UpdateFoundOnBoot) {

                if (getWindow(app::window::name::desktop_update)) {
                    std::unique_ptr<gui::UpdateSwitchData> data = std::make_unique<gui::UpdateSwitchData>(updateMsg);
                    switchWindow(app::window::name::desktop_update, gui::ShowMode::GUI_SHOW_INIT, std::move(data));
                }
            }

            if (updateMsg != nullptr && updateMsg->messageType == updateos::UpdateMessageType::UpdateNow) {
                auto data = std::make_unique<gui::UpdateSwitchData>(updateMsg);
                switchWindow(app::window::name::desktop_update_progress, gui::ShowMode::GUI_SHOW_INIT, std::move(data));
            }

            if (updateMsg != nullptr && updateMsg->messageType == updateos::UpdateMessageType::UpdateInform) {
                if (getWindow(app::window::name::desktop_update)) {
                    std::unique_ptr<gui::UpdateSwitchData> data = std::make_unique<gui::UpdateSwitchData>(updateMsg);
                    getWindow(app::window::name::desktop_update)->handleSwitchData(data.get());
                }
            }
            return sys::msgHandled();
        });

        connect(typeid(db::NotificationMessage), [&](sys::Message *request) {
            auto notificationMessage = static_cast<db::NotificationMessage *>(request);
            dbNotificationHandler.handle(notificationMessage);
            return sys::MessageNone{};
        });

        auto msgToSend =
            std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateCheckForUpdateOnce);
        bus.sendUnicast(msgToSend, service::name::service_desktop);

        settings->registerValueChange(
            settings::SystemProperties::osCurrentVersion,
            [this](const std::string &value) { osCurrentVersionChanged(value); },
            settings::SettingsScope::Global);

        settings->registerValueChange(
            settings::SystemProperties::osUpdateVersion,
            [this](const std::string &value) { osUpdateVersionChanged(value); },
            settings::SettingsScope::Global);

        dbNotificationHandler.initHandler();

        return sys::ReturnCodes::Success;


@@ 244,15 186,6 @@ namespace app
            auto presenter = std::make_unique<gui::PowerOffPresenter>(app);
            return std::make_unique<gui::RebootWindow>(app, std::move(presenter));
        });
        windowsFactory.attach(desktop_update, [](Application *app, const std::string newname) {
            return std::make_unique<gui::UpdateWindow>(app);
        });
        windowsFactory.attach(desktop_update_progress, [](Application *app, const std::string newname) {
            return std::make_unique<gui::UpdateProgressWindow>(app);
        });
        windowsFactory.attach(desktop_post_update_window, [](Application *app, const std::string newname) {
            return std::make_unique<gui::PostUpdateWindow>(app);
        });
        windowsFactory.attach(desktop_mmi_pull, [](Application *app, const std::string newname) {
            return std::make_unique<gui::MmiPullWindow>(app, desktop_mmi_pull);
        });


@@ 308,32 241,4 @@ namespace app
            }
        }
    }

    void ApplicationDesktop::osUpdateVersionChanged(const std::string &value)
    {
        LOG_DEBUG("[ApplicationDesktop::osUpdateVersionChanged] value=%s", value.c_str());
        if (value.empty()) {
            return;
        }
        osUpdateVersion = value;
    }

    void ApplicationDesktop::osCurrentVersionChanged(const std::string &value)
    {
        LOG_DEBUG("[ApplicationDesktop::osCurrentVersionChanged] value=%s", value.c_str());
        if (value.empty()) {
            return;
        }
        osCurrentVersion = value;
    }
    void ApplicationDesktop::setOsUpdateVersion(const std::string &value)
    {
        LOG_DEBUG("[ApplicationDesktop::setOsUpdateVersion] value=%s", value.c_str());
        if (value.empty()) {
            return;
        }
        osUpdateVersion = value;
        settings->setValue(settings::SystemProperties::osUpdateVersion, value, settings::SettingsScope::Global);
    }

} // namespace app

M module-apps/application-desktop/CMakeLists.txt => module-apps/application-desktop/CMakeLists.txt +0 -6
@@ 43,14 43,8 @@ target_sources(application-desktop
		windows/MmiPullWindow.hpp
		windows/MmiPushWindow.cpp
		windows/MmiPushWindow.hpp
		windows/PostUpdateWindow.cpp
		windows/PostUpdateWindow.hpp
		windows/Reboot.cpp
		windows/Reboot.hpp
		windows/Update.cpp
		windows/Update.hpp
		windows/UpdateProgress.cpp
		windows/UpdateProgress.hpp
	PUBLIC
		include/application-desktop/ApplicationDesktop.hpp
		include/application-desktop/Constants.hpp

M module-apps/application-desktop/data/DesktopData.hpp => module-apps/application-desktop/data/DesktopData.hpp +0 -16
@@ 11,22 11,6 @@

namespace gui
{

    class UpdateSwitchData : public gui::SwitchData
    {
      public:
        explicit UpdateSwitchData(sdesktop::UpdateOsMessage *messageToCopyFrom) : updateOsMessage(*messageToCopyFrom)
        {}

        [[nodiscard]] const sdesktop::UpdateOsMessage &getUpdateOsMessage() const noexcept
        {
            return updateOsMessage;
        }

      private:
        sdesktop::UpdateOsMessage updateOsMessage;
    };

    class CurrentOsVersion : public gui::SwitchData
    {
        std::string osVersion;

M module-apps/application-desktop/include/application-desktop/ApplicationDesktop.hpp => module-apps/application-desktop/include/application-desktop/ApplicationDesktop.hpp +0 -13
@@ 44,26 44,13 @@ namespace app
        // if there is modem notification and there is no default SIM selected, then we need to select if when unlock is
        // done
        void handle(cellular::msg::notification::ModemStateChanged *msg);
        auto handle(sdesktop::UpdateOsMessage *msg) -> bool;
        void handleNotificationsChanged(std::unique_ptr<gui::SwitchData> notificationsParams) override;

        std::string getOsUpdateVersion() const
        {
            return osUpdateVersion;
        }
        std::string getOsCurrentVersion() const
        {
            return osCurrentVersion;
        }
        void setOsUpdateVersion(const std::string &value);

      private:
        bool refreshMenuWindow();
        void handleLowBatteryNotification(manager::actions::ActionParamsPtr &&data);
        void osUpdateVersionChanged(const std::string &value);
        void osCurrentVersionChanged(const std::string &value);
        std::string osUpdateVersion{updateos::initSysVer};
        std::string osCurrentVersion{updateos::initSysVer};
        DBNotificationsHandler dbNotificationHandler;
    };


M module-apps/application-desktop/windows/DesktopMainWindow.cpp => module-apps/application-desktop/windows/DesktopMainWindow.cpp +0 -11
@@ 95,9 95,6 @@ namespace gui
        : AppWindow(app, app::window::name::desktop_main_window),
          notificationsModel(std::make_shared<ActiveNotificationsModel>(this))
    {
        osUpdateVer  = getAppDesktop()->getOsUpdateVersion();
        osCurrentVer = getAppDesktop()->getOsCurrentVersion();

        buildInterface();

        preBuildDrawListHook = [this](std::list<Command> &cmd) { updateTime(); };


@@ 106,14 103,6 @@ namespace gui
    void DesktopMainWindow::setVisibleState()
    {
        setActiveState();

        if (osUpdateVer == osCurrentVer && osUpdateVer != updateos::initSysVer &&
            osCurrentVer != updateos::initSysVer) {
            auto data = std::make_unique<CurrentOsVersion>();
            data->setData(osCurrentVer);
            application->switchWindow(app::window::name::desktop_post_update_window, std::move(data));
            getAppDesktop()->setOsUpdateVersion(updateos::initSysVer);
        }
    }

    void DesktopMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)

M module-apps/application-desktop/windows/DesktopMainWindow.hpp => module-apps/application-desktop/windows/DesktopMainWindow.hpp +0 -2
@@ 53,8 53,6 @@ namespace gui
        bool resolveDialAction(const std::string &number);
        bool showInformationPopup(std::function<bool()> action, const std::string &notification);
        void invalidate() noexcept;
        std::string osUpdateVer;
        std::string osCurrentVer;

        gui::KeyInputMappedTranslation translator;
    };

D module-apps/application-desktop/windows/PostUpdateWindow.cpp => module-apps/application-desktop/windows/PostUpdateWindow.cpp +0 -87
@@ 1,87 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DesktopData.hpp"
#include "Names.hpp"
#include "PostUpdateWindow.hpp"

#include <i18n/i18n.hpp>
#include <locks/data/LockStyle.hpp>

using namespace gui;

PostUpdateWindow::PostUpdateWindow(app::Application *app)
    : AppWindow(app, app::window::name::desktop_post_update_window)
{
    buildInterface();
}

void PostUpdateWindow::onBeforeShow(ShowMode mode, SwitchData *data)
{
    if (data == nullptr) {
        LOG_ERROR("Received null pointer");
    }
    else {
        auto *item = dynamic_cast<CurrentOsVersion *>(data);
        if (item != nullptr) {
            currentOsVersion = item->getCurrentOsVersion();
            auto info        = utils::translate("app_desktop_update_success");
            utils::findAndReplaceAll(info, "$VERSION", currentOsVersion);
            infoIcon->text->setText(info);
        }
    }
    setVisibleState();
}

void PostUpdateWindow::setVisibleState()
{
    bottomBar->setActive(BottomBar::Side::CENTER, true);
}

bool PostUpdateWindow::onInput(const InputEvent &inputEvent)
{
    if (inputEvent.isShortRelease(KeyCode::KEY_ENTER) && bottomBar->isActive(BottomBar::Side::CENTER)) {
        application->switchWindow(gui::name::window::main_window);
        return true;
    }
    return AppWindow::onInput(inputEvent);
}

void PostUpdateWindow::rebuild()
{
    destroyInterface();
    buildInterface();
}

status_bar::Configuration PostUpdateWindow::configureStatusBar(status_bar::Configuration appConfiguration)
{
    appConfiguration.enable(status_bar::Indicator::Time);
    appConfiguration.disable(status_bar::Indicator::Lock);
    appConfiguration.disable(status_bar::Indicator::Battery);
    appConfiguration.disable(status_bar::Indicator::Signal);
    appConfiguration.disable(status_bar::Indicator::SimCard);
    return appConfiguration;
}

void PostUpdateWindow::buildInterface()
{
    AppWindow::buildInterface();

    setTitle(utils::translate("app_desktop_update_muditaos"));

    bottomBar->setText(BottomBar::Side::CENTER, utils::translate("common_ok"));

    infoIcon = new gui::Icon(this,
                             style::window::default_left_margin,
                             style::window::default_vertical_pos,
                             style::window::default_body_width,
                             style::window::default_body_height,
                             "circle_success",
                             "");
    infoIcon->setAlignment(Alignment::Horizontal::Center);
}

void PostUpdateWindow::destroyInterface()
{
    erase();
}

D module-apps/application-desktop/windows/PostUpdateWindow.hpp => module-apps/application-desktop/windows/PostUpdateWindow.hpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/windows/AppWindow.hpp>
#include <module-gui/gui/widgets/Icon.hpp>
#include <module-gui/gui/widgets/Text.hpp>

namespace gui
{
    class PostUpdateWindow : public AppWindow
    {
        Icon *infoIcon = nullptr;

        void setVisibleState();
        std::string currentOsVersion;

      public:
        explicit PostUpdateWindow(app::Application *app);
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const InputEvent &inputEvent) override;

        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;
        status_bar::Configuration configureStatusBar(status_bar::Configuration appConfiguration) override;
    };
} /* namespace gui */

D module-apps/application-desktop/windows/Update.cpp => module-apps/application-desktop/windows/Update.cpp +0 -272
@@ 1,272 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationDesktop.hpp"
#include "Constants.hpp"
#include "DesktopData.hpp"
#include "Update.hpp"

#include <boot/bootconfig.hpp>
#include <i18n/i18n.hpp>
#include <log.hpp>
#include <service-appmgr/model/ApplicationManager.hpp>
#include <service-cellular/ServiceCellular.hpp>
#include <service-desktop/Constants.hpp>
#include <source/version.hpp>
#include <Style.hpp>

namespace gui
{

    UpdateWindow::UpdateWindow(app::Application *app) : AppWindow(app, app::window::name::desktop_update)
    {
        buildInterface();
    }

    void UpdateWindow::rebuild()
    {
        // find which widget has focus
        uint32_t index = 0;
        for (uint32_t i = 0; i < selectionLabels.size(); i++)
            if (selectionLabels[i] == getFocusItem()) {
                index = i;
                break;
            }

        destroyInterface();
        buildInterface();
        setFocusItem(selectionLabels[index]);
    }
    void UpdateWindow::buildInterface()
    {
        AppWindow::buildInterface();
        bottomBar->setActive(BottomBar::Side::CENTER, true);
        bottomBar->setActive(BottomBar::Side::RIGHT, true);
        bottomBar->setText(BottomBar::Side::CENTER, utils::translate(style::strings::common::confirm));
        bottomBar->setText(BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));

        // title label
        titleLabel = new gui::Label(this, 0, 60, 480, 40);
        titleLabel->setFilled(false);
        titleLabel->setBorderColor(gui::ColorFullBlack);
        titleLabel->setFont(style::header::font::title);
        titleLabel->setEdges(RectangleEdge::None);
        titleLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Top));
        titleLabel->setText(utils::translate("app_desktop_update"));

        // Update version information
        updateVersionInfo = new gui::Label(this, 10, 132, 480, 40);
        updateVersionInfo->setFilled(false);
        updateVersionInfo->setBorderColor(gui::ColorFullBlack);
        updateVersionInfo->setFont(style::window::font::smallbold);
        updateVersionInfo->setEdges(RectangleEdge::None);
        updateVersionInfo->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        updateVersionInfo->setText(utils::translate("app_desktop_update"));

        // Update details
        updateDetails = new gui::Label(this, 40, 172, 440, 40);
        updateDetails->setFilled(false);
        updateDetails->setBorderColor(gui::ColorFullBlack);
        updateDetails->setFont(style::window::font::verysmall);
        updateDetails->setEdges(RectangleEdge::None);
        updateDetails->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        updateDetails->setText(utils::translate("app_desktop_update"));

        // Current version information
        currentVersionInfo = new gui::Label(this, 10, 222, 480, 40);
        currentVersionInfo->setFilled(false);
        currentVersionInfo->setBorderColor(gui::ColorFullBlack);
        currentVersionInfo->setFont(style::window::font::small);
        currentVersionInfo->setEdges(RectangleEdge::None);
        currentVersionInfo->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        currentVersionInfo->setText(utils::translate("app_desktop_update_current"));

        // Label Info
        infoLabel = new gui::Label(this, 20, 304, 440, 40);
        infoLabel->setFilled(false);
        infoLabel->setBorderColor(gui::ColorNoColor);
        infoLabel->setFont(style::window::font::medium);
        infoLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Bottom));
        infoLabel->setText(utils::translate("app_desktop_update_apply"));

        // Details during update
        detailLabel = new gui::Label(this, 20, 354, 440, 20);
        detailLabel->setFilled(false);
        detailLabel->setBorderColor(gui::ColorNoColor);
        detailLabel->setFont(style::window::font::small);
        detailLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Bottom));

        // update progress
        percentLabel = new gui::Label(this, 0, 374, 520, 128);
        percentLabel->setFilled(false);
        percentLabel->setBorderColor(gui::ColorNoColor);
        percentLabel->setFont(style::window::font::largelight);
        percentLabel->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Bottom));
        percentLabel->setVisible(false);

        uint32_t pinLabelX = 46;
        uint32_t pinLabelY = 410;
        for (uint32_t i = 0; i < 4; i++) {
            gui::Label *label = new gui::Label(this, pinLabelX, pinLabelY, 193, 75);
            label->setFilled(false);
            label->setBorderColor(gui::ColorFullBlack);
            label->setPenWidth(0);
            label->setPenFocusWidth(2);
            label->setRadius(5);
            label->setFont(style::window::font::medium);
            label->setEdges(RectangleEdge::All);
            label->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
            selectionLabels.push_back(label);
            pinLabelX += 193;
        }
        selectionLabels[0]->setText(utils::translate(style::strings::common::no));
        selectionLabels[1]->setText(utils::translate(style::strings::common::yes));

        // define navigation between labels
        selectionLabels[0]->setNavigationItem(NavigationDirection::LEFT, selectionLabels[1]);
        selectionLabels[0]->setNavigationItem(NavigationDirection::RIGHT, selectionLabels[1]);

        selectionLabels[1]->setNavigationItem(NavigationDirection::LEFT, selectionLabels[0]);
        selectionLabels[1]->setNavigationItem(NavigationDirection::RIGHT, selectionLabels[0]);

        // callbacks for getting focus
        selectionLabels[0]->focusChangedCallback = [=](gui::Item &item) {
            if (item.focus)
                this->state = State::Return;
            return true;
        };

        selectionLabels[1]->focusChangedCallback = [=](gui::Item &item) {
            if (item.focus)
                this->state = State::UpdateNow;
            return true;
        };
    }
    void UpdateWindow::destroyInterface()
    {
        erase();
    }

    void UpdateWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (data == nullptr) {
            LOG_ERROR("Received null pointer");
        }
        else {
            gui::UpdateSwitchData *item = dynamic_cast<gui::UpdateSwitchData *>(data);
            if (item != nullptr) {
                std::stringstream currentVersion;
                std::stringstream updateVersion;
                std::stringstream updateFileDetails;

                sdesktop::UpdateOsMessage msg = item->getUpdateOsMessage();
                updateFile                    = msg.updateStats.updateFile;
                currentVersion << "Current: ";
                currentVersion << VERSION;
                currentVersion << " (";
                currentVersion << GIT_REV;
                currentVersion << ")";

                updateVersion << utils::translate("app_desktop_update_to");
                updateVersion << ": ";
                updateVersion << msg.updateStats.versionInformation[boot::json::os_version][boot::json::version_string]
                                     .string_value();
                updateVersion << " (";
                updateVersion << msg.updateStats.versionInformation[boot::json::git_info][boot::json::os_git_revision]
                                     .string_value();
                updateVersion << ")";

                updateFileDetails << utils::translate("app_desktop_update_size");
                updateFileDetails << ": ";
                updateFileDetails << std::to_string(msg.updateStats.totalBytes / 1024);
                updateFileDetails << "Kb (";
                updateFileDetails
                    << msg.updateStats.versionInformation[boot::json::misc][boot::json::builddate].string_value();
                updateFileDetails << ")";

                currentVersionInfo->setText(currentVersion.str());
                updateVersionInfo->setText(updateVersion.str());
                updateDetails->setText(updateFileDetails.str());
            }
        }

        state = State::Return;
        setFocusItem(selectionLabels[0]);
    }

    bool UpdateWindow::onInput(const InputEvent &inputEvent)
    {
        // check if any of the lower inheritance onInput methods catch the event
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        // process only short press, consume rest
        if (!inputEvent.isShortRelease())
            return true;

        // if enter was pressed check state and power down or return to main desktop's window
        if (inputEvent.is(KeyCode::KEY_ENTER)) {
            if (state == State::Return) {
                application->switchWindow(app::window::name::desktop_main_window);
            }
            if (state == State::UpdateNow) {
                bottomBar->setActive(BottomBar::Side::CENTER, false);
                bottomBar->setActive(BottomBar::Side::RIGHT, false);

                selectionLabels[0]->setVisible(false);
                selectionLabels[1]->setVisible(false);

                percentLabel->setVisible(true);
                percentLabel->setText(utils::translate("app_desktop_update_start"));
                auto msgToSend = std::make_shared<sdesktop::UpdateOsMessage>(updateFile.c_str(), 0);
                application->bus.sendUnicast(msgToSend, service::name::service_desktop);

                return true;
            }
        }

        return false;
    }

    bool UpdateWindow::handleSwitchData(SwitchData *data)
    {
        gui::UpdateSwitchData *item = dynamic_cast<gui::UpdateSwitchData *>(data);
        if (item != nullptr) {
            std::stringstream ssi;
            std::stringstream sizeStream;

            updateos::UpdateState status =
                static_cast<updateos::UpdateState>(item->getUpdateOsMessage().updateStats.status);

            if (status == updateos::UpdateState::ExtractingFiles) {
                progressPercent =
                    static_cast<int>((static_cast<float>(item->getUpdateOsMessage().updateStats.currentExtractedBytes) /
                                      static_cast<float>(item->getUpdateOsMessage().updateStats.totalBytes)) *
                                     100.0);
                ssi << utils::translate("app_desktop_update_unpacking");
                ssi << ": ";
                ssi << std::to_string(progressPercent);
                ssi << " %";
                percentLabel->setText(ssi.str());
                infoLabel->setText(item->getUpdateOsMessage().updateStats.messageText);
            }
            else if (item->getUpdateOsMessage().updateStats.messageText != "") {
                percentLabel->setText(item->getUpdateOsMessage().updateStats.messageText);
            }

            sizeStream << utils::translate("app_desktop_update_size");
            sizeStream << ": ";
            sizeStream << std::to_string(item->getUpdateOsMessage().updateStats.fileExtractedSize);
            sizeStream << " ";
            sizeStream << utils::translate("app_desktop_update_bytes");
            detailLabel->setText(sizeStream.str());
            this->application->refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
        }

        return true;
    }
} /* namespace gui */

D module-apps/application-desktop/windows/Update.hpp => module-apps/application-desktop/windows/Update.hpp +0 -50
@@ 1,50 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <AppWindow.hpp>
#include <gui/widgets/BottomBar.hpp>
#include <gui/widgets/Image.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/TextFixedSize.hpp>

#include <vector>

namespace gui
{
    class UpdateWindow : public AppWindow
    {

        enum class State
        {
            UpdateNow,
            Return,
        };

        gui::Label *titleLabel   = nullptr;
        gui::Label *infoLabel    = nullptr;
        gui::Label *detailLabel  = nullptr;
        gui::Label *percentLabel = nullptr;

        gui::Label *currentVersionInfo = nullptr;
        gui::Label *updateVersionInfo  = nullptr;
        gui::Label *updateDetails      = nullptr;

        std::vector<gui::Label *> selectionLabels;
        State state         = State::Return;
        int progressPercent = 0;

      public:
        UpdateWindow(app::Application *app);
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const InputEvent &inputEvent) override;
        bool handleSwitchData(SwitchData *data) override;
        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;

        fs::path updateFile;
    };

} /* namespace gui */

D module-apps/application-desktop/windows/UpdateProgress.cpp => module-apps/application-desktop/windows/UpdateProgress.cpp +0 -177
@@ 1,177 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationDesktop.hpp"
#include "DesktopData.hpp"
#include "DesktopStyle.hpp"
#include "UpdateProgress.hpp"

#include <boot/bootconfig.hpp>
#include <DialogMetadataMessage.hpp>
#include <i18n/i18n.hpp>
#include <log.hpp>
#include <service-appmgr/model/ApplicationManager.hpp>
#include <source/version.hpp>
#include <Style.hpp>

namespace gui
{
    UpdateProgressWindow::UpdateProgressWindow(app::Application *app)
        : AppWindow(app, app::window::name::desktop_update_progress)
    {
        buildInterface();
        preventsAutoLock = true;
    }

    void UpdateProgressWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (data == nullptr) {
            LOG_ERROR("Received null pointer");
        }
        else {
            auto *item = dynamic_cast<gui::UpdateSwitchData *>(data);
            if (item != nullptr) {
                const auto &msg = item->getUpdateOsMessage();
                updateFile = msg.updateStats.updateFile;
                auto updateVersion =
                    msg.updateStats.versionInformation[boot::json::os_version][boot::json::version_string]
                        .string_value();
                if (text->getText().empty()) {
                    text->setText(utils::translate("app_desktop_update_preparing") + " " + updateVersion);
                }
                else {
                    text->setText(textInfo);
                }
            }
            percentLabel->setText(std::to_string(progressPercent) + " %");
            updateProgress->setPercentageValue(progressPercent);
        }
        setVisibleState();
    }

    bool UpdateProgressWindow::onInput(const InputEvent & /*inputEvent*/)
    {
        return true;
    }

    void UpdateProgressWindow::setVisibleState()
    {
        percentLabel->setVisible(true);
        updateProgress->setVisible(true);
        updateProgress->setVisible(true);
        text->setVisible(true);
    }

    void UpdateProgressWindow::rebuild()
    {
        destroyInterface();
        buildInterface();
    }

    status_bar::Configuration UpdateProgressWindow::configureStatusBar(status_bar::Configuration appConfiguration)
    {
        appConfiguration.enable(status_bar::Indicator::Time);
        appConfiguration.disable(status_bar::Indicator::Lock);
        appConfiguration.disable(status_bar::Indicator::Battery);
        appConfiguration.disable(status_bar::Indicator::Signal);
        appConfiguration.disable(status_bar::Indicator::SimCard);
        return appConfiguration;
    }

    void UpdateProgressWindow::buildInterface()
    {
        AppWindow::buildInterface();

        setTitle(utils::translate("app_desktop_update_muditaos"));

        icon = new Image(this, style::desktop::image::x, style::desktop::image::y, "circle_update");

        text = new Text(this,
                        style::desktop::textupdate::x,
                        style::desktop::textupdate::y,
                        style::desktop::textupdate::w,
                        style::desktop::textupdate::h);
        text->setTextType(TextType::MultiLine);
        text->setEditMode(EditMode::Browse);
        text->setEdges(RectangleEdge::None);
        text->setFont(style::window::font::biglight);
        text->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));

        percentLabel = new gui::Label(this,
                                      style::desktop::percentlabel::x,
                                      style::desktop::percentlabel::y,
                                      style::desktop::percentlabel::w,
                                      style::desktop::percentlabel::h);
        percentLabel->setFilled(false);
        percentLabel->setBorderColor(gui::ColorNoColor);
        percentLabel->setFont(style::window::font::biglight);
        percentLabel->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));

        updateProgress = new HBarGraph(this,
                                       style::window::progressBar::x,
                                       style::window::progressBar::y,
                                       style::window::progressBar::range,
                                       BarGraphStyle::Light);
        updateProgress->setSize(style::window::progressBar::w, style::window::progressBar::h);
        updateProgress->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
    }

    void UpdateProgressWindow::destroyInterface()
    {
        erase();
        invalidate();
    }

    void UpdateProgressWindow::invalidate() noexcept
    {
        percentLabel   = nullptr;
        updateProgress = nullptr;
        text           = nullptr;
        icon           = nullptr;
    }

    bool UpdateProgressWindow::handleSwitchData(SwitchData *data)
    {
        auto *item = dynamic_cast<gui::UpdateSwitchData *>(data);
        if (item != nullptr) {
            auto status = static_cast<updateos::UpdateState>(item->getUpdateOsMessage().updateStats.status);
            switch (status) {
            case updateos::UpdateState::Initial:
                textInfo        = text->getText();
                progressPercent = 10;
                break;
            case updateos::UpdateState::UpdateFileSet:
                textInfo        = text->getText();
                progressPercent = 20;
                break;
            case updateos::UpdateState::CreatingDirectories:
                textInfo        = text->getText();
                progressPercent = 30;
                break;
            case updateos::UpdateState::ExtractingFiles:
                textInfo        = utils::translate("app_desktop_update_in_progress");
                progressPercent = 40;
                break;
            case updateos::UpdateState::ChecksumVerification:
                textInfo        = text->getText();
                progressPercent = 70;
                break;
            case updateos::UpdateState::VersionVerificiation:
                textInfo        = text->getText();
                progressPercent = 80;
                break;
            case updateos::UpdateState::UpdatingBootloader:
                textInfo        = utils::translate("app_desktop_update_ready_for_reset");
                progressPercent = 99;
                break;
            case updateos::UpdateState::ReadyForReset:
                textInfo        = text->getText();
                progressPercent = 100;
                break;
            }
        }
        return true;
    }
} /* namespace gui */

D module-apps/application-desktop/windows/UpdateProgress.hpp => module-apps/application-desktop/windows/UpdateProgress.hpp +0 -46
@@ 1,46 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <apps-common/widgets/BarGraph.hpp>
#include <apps-common/windows/AppWindow.hpp>
#include <apps-common/windows/Dialog.hpp>
#include <module-gui/gui/widgets/BottomBar.hpp>
#include <module-gui/gui/widgets/Image.hpp>
#include <module-gui/gui/widgets/Label.hpp>
#include <module-gui/gui/widgets/TextFixedSize.hpp>

#include <vector>

namespace gui
{

    class UpdateProgressWindow : public AppWindow
    {
        unsigned int progressPercent = 0;
        std::string textInfo;

        gui::Label *percentLabel  = nullptr;
        HBarGraph *updateProgress = nullptr;

        fs::path updateFile;

        Text *text  = nullptr;
        Image *icon = nullptr;

        void setVisibleState();
        void invalidate() noexcept;

      public:
        explicit UpdateProgressWindow(app::Application *app);
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const InputEvent &inputEvent) override;
        bool handleSwitchData(SwitchData *data) override;
        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;
        status_bar::Configuration configureStatusBar(status_bar::Configuration appConfiguration) override;
    };

} /* namespace gui */

M module-bsp/bsp/usb/usb.hpp => module-bsp/bsp/usb/usb.hpp +0 -11
@@ 29,20 29,9 @@ namespace bsp
        Configured
    };

    class USBDeviceListener
    {
      public:
        virtual bool getRawMode() const noexcept
        {
            return false;
        }
        virtual void rawDataReceived(void *dataPtr, uint32_t dataLen) = 0;
    };

    struct usbInitParams {
        xQueueHandle queueHandle;
        xQueueHandle irqQueueHandle;
        USBDeviceListener *deviceListener;
        const char *serialNumber;
    };


M module-services/service-appmgr/CMakeLists.txt => module-services/service-appmgr/CMakeLists.txt +0 -4
@@ 18,13 18,11 @@ target_sources(service-appmgr
        messages/LanguageChangeRequest.cpp
        messages/PowerSaveModeInitRequest.cpp
        messages/PreventBlockingRequest.cpp
        messages/SetOsUpdateVersion.cpp
        messages/ShutdownRequest.cpp
        messages/StartAllowedMessage.cpp
        messages/SwitchBackRequest.cpp
        messages/SwitchConfirmation.cpp
        messages/SwitchRequest.cpp
        messages/UpdateInProgress.cpp
        model/ActionsRegistry.cpp
        model/ApplicationHandle.cpp
        model/ApplicationManager.cpp


@@ 55,13 53,11 @@ target_sources(service-appmgr
        include/service-appmgr/messages/Message.hpp
        include/service-appmgr/messages/PowerSaveModeInitRequest.hpp
        include/service-appmgr/messages/PreventBlockingRequest.hpp
        include/service-appmgr/messages/SetOsUpdateVersion.hpp
        include/service-appmgr/messages/ShutdownRequest.hpp
        include/service-appmgr/messages/StartAllowedMessage.hpp
        include/service-appmgr/messages/SwitchBackRequest.hpp
        include/service-appmgr/messages/SwitchConfirmation.hpp
        include/service-appmgr/messages/SwitchRequest.hpp
        include/service-appmgr/messages/UpdateInProgress.hpp
        include/service-appmgr/messages/UserPowerDownRequest.hpp
        include/service-appmgr/model/ActionsRegistry.hpp
        include/service-appmgr/model/ApplicationHandle.hpp

M module-services/service-appmgr/include/service-appmgr/messages/Message.hpp => module-services/service-appmgr/include/service-appmgr/messages/Message.hpp +0 -2
@@ 19,5 19,3 @@
#include "ShutdownRequest.hpp"
#include "GetCurrentDisplayLanguageRequest.hpp"
#include "GetCurrentDisplayLanguageResponse.hpp"
#include "UpdateInProgress.hpp"
#include "SetOsUpdateVersion.hpp"

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

#pragma once

#include "BaseMessage.hpp"

namespace app::manager
{
    class SetOsUpdateVersion : public BaseMessage
    {
      public:
        explicit SetOsUpdateVersion(const ApplicationName &sender,
                                    const std::string &osUpdateVer,
                                    const std::string &osCurrentVer);
        std::string osUpdateVer{};
        std::string osCurrentVer{};
    };

} // namespace app::manager

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

#pragma once

#include "BaseMessage.hpp"

namespace app::manager
{
    class UpdateInProgress : public BaseMessage
    {
      public:
        explicit UpdateInProgress(const ApplicationName &sender);
    };
} // namespace app::manager

M module-services/service-appmgr/include/service-appmgr/model/ApplicationManager.hpp => module-services/service-appmgr/include/service-appmgr/model/ApplicationManager.hpp +0 -1
@@ 145,7 145,6 @@ namespace app::manager
        auto handleInitApplication(ApplicationInitialised *msg) -> bool;
        auto handleDisplayLanguageChange(DisplayLanguageChangeRequest *msg) -> bool;
        auto handleInputLanguageChange(InputLanguageChangeRequest *msg) -> bool;
        auto handleSetOsUpdateVersionChange(SetOsUpdateVersion *msg) -> bool;
        auto handleDBResponse(db::QueryResponse *msg) -> bool;
        auto handlePowerSavingModeInit() -> bool;
        auto handleMessageAsAction(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>;

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

#include "SetOsUpdateVersion.hpp"

namespace app::manager
{
    SetOsUpdateVersion::SetOsUpdateVersion(const ApplicationName &sender,
                                           const std::string &osUpdateVer,
                                           const std::string &osCurrentVer)
        : BaseMessage(MessageType::UpdateOS, sender), osUpdateVer(osUpdateVer), osCurrentVer(osCurrentVer)
    {}

} // namespace app::manager

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

#include "UpdateInProgress.hpp"

namespace app::manager
{
    UpdateInProgress::UpdateInProgress(const ApplicationName &sender)
        : BaseMessage(MessageType::APMDelayedClose, sender)
    {}

} // namespace app::manager

M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +0 -20
@@ 350,15 350,6 @@ namespace app::manager
        connect(typeid(GetCurrentDisplayLanguageRequest), [&](sys::Message *request) {
            return std::make_shared<GetCurrentDisplayLanguageResponse>(utils::getDisplayLanguage());
        });
        connect(typeid(UpdateInProgress), [this](sys::Message *) {
            closeApplicationsOnUpdate();
            return sys::msgHandled();
        });
        connect(typeid(SetOsUpdateVersion), [this](sys::Message *request) {
            auto msg = static_cast<SetOsUpdateVersion *>(request);
            handleSetOsUpdateVersionChange(msg);
            return sys::msgHandled();
        });
        connect(typeid(GetAllNotificationsRequest), [&](sys::Message *request) {
            notificationProvider.requestNotSeenNotifications();
            notificationProvider.send();


@@ 1005,17 996,6 @@ namespace app::manager
        return true;
    }

    auto ApplicationManager::handleSetOsUpdateVersionChange(SetOsUpdateVersion *msg) -> bool
    {
        LOG_DEBUG("[ApplicationManager::handleSetOsUpdateVersionChange] value ....");
        settings->setValue(
            settings::SystemProperties::osUpdateVersion, msg->osUpdateVer, settings::SettingsScope::Global);

        settings->setValue(
            settings::SystemProperties::osCurrentVersion, msg->osCurrentVer, settings::SettingsScope::Global);
        return true;
    }

    auto ApplicationManager::handleDBResponse(db::QueryResponse *msg) -> bool
    {
        auto result = msg->getResult();

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +1 -2
@@ 18,7 18,6 @@ set(SOURCES
    endpoints/developerMode/DeveloperModeEndpoint.cpp
    endpoints/developerMode/DeveloperModeHelper.cpp
    endpoints/developerMode/Mode/UI_Helper.cpp
    endpoints/developerMode/Mode/UpdateHelper.cpp
    endpoints/developerMode/event/DomRequest.cpp
    endpoints/developerMode/event/ATRequest.cpp
    endpoints/deviceInfo/DeviceInfoEndpoint.cpp


@@ 28,7 27,7 @@ set(SOURCES
    endpoints/messages/MessagesEndpoint.cpp
    endpoints/restore/RestoreEndpoint.cpp
    endpoints/update/UpdateEndpoint.cpp
    endpoints/update/UpdateMuditaOS.cpp
    endpoints/update/UpdateHelper.cpp
    endpoints/filesystem/FilesystemEndpoint.cpp
    endpoints/filesystem/FileOperations.cpp
    endpoints/filesystem/FileContext.cpp

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +0 -72
@@ 8,7 8,6 @@
#include "service-cellular/CellularMessage.hpp"
#include "endpoints/factoryReset/FactoryReset.hpp"
#include "endpoints/backup/BackupRestore.hpp"
#include "endpoints/update/UpdateMuditaOS.hpp"

#include <Common/Query.hpp>
#include <MessageType.hpp>


@@ 69,8 68,6 @@ ServiceDesktop::ServiceDesktop()
{
    LOG_INFO("[ServiceDesktop] Initializing");
    bus.channels.push_back(sys::BusChannel::PhoneLockChanges);

    updateOS         = std::make_unique<UpdateMuditaOS>(this);
}

ServiceDesktop::~ServiceDesktop()


@@ 128,12 125,6 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        desktopWorker->run();
    }

    transferTimer =
        sys::TimerFactory::createPeriodicTimer(this,
                                               "WorkerDesktop file upload",
                                               std::chrono::milliseconds{sdesktop::file_transfer_timeout},
                                               [this](sys::Timer &) { desktopWorker->cancelTransferOnTimeout(); });

    connect(sdesktop::developerMode::DeveloperModeRequest(), [&](sys::Message *msg) {
        auto request = static_cast<sdesktop::developerMode::DeveloperModeRequest *>(msg);
        if (request->event != nullptr) {


@@ 190,62 181,6 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        return sys::MessageNone{};
    });

    connect(sdesktop::UpdateOsMessage(), [&](sys::Message *msg) {
        sdesktop::UpdateOsMessage *updateOsMsg = dynamic_cast<sdesktop::UpdateOsMessage *>(msg);

        if (updateOsMsg != nullptr &&
            updateOsMsg->messageType == updateos::UpdateMessageType::UpdateCheckForUpdateOnce) {
            fs::path file = UpdateMuditaOS::checkForUpdate();

            if (file.has_filename()) {
                /* send info to applicationDesktop that there is an update waiting */
                auto msgToSend =
                    std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateFoundOnBoot, file);
                msgToSend->updateStats.versionInformation = UpdateMuditaOS::getVersionInfoFromFile(file);
                bus.sendUnicast(msgToSend, app::name_desktop);
            }
        }

        if (updateOsMsg != nullptr && updateOsMsg->messageType == updateos::UpdateMessageType::UpdateNow) {
            LOG_DEBUG("ServiceDesktop::DataReceivedHandler file:%s uuid:%" PRIu32 "",
                      updateOsMsg->updateStats.updateFile.c_str(),
                      updateOsMsg->updateStats.uuid);

            if (updateOS->setUpdateFile(purefs::dir::getUpdatesOSPath(), updateOsMsg->updateStats.updateFile) ==
                updateos::UpdateError::NoError) {
                RemountFS();
                // Same possible issue as with FactoryReset::Run()
                updateOS->runUpdate();
            }
        }

        return sys::MessageNone{};
    });

    connect(sdesktop::transfer::TransferTimerState(), [&](sys::Message *msg) {
        sdesktop::transfer::TransferTimerState *timerStateMsg =
            dynamic_cast<sdesktop::transfer::TransferTimerState *>(msg);

        if (timerStateMsg != nullptr && timerStateMsg->messageType == MessageType::TransferTimer) {
            switch (timerStateMsg->req) {
            case sdesktop::transfer::TransferTimerState::Start:
                [[fallthrough]];
            case sdesktop::transfer::TransferTimerState::Reload:
                transferTimer.start();
                break;
            case sdesktop::transfer::TransferTimerState::Stop:
                transferTimer.stop();
                break;
            case sdesktop::transfer::TransferTimerState::None:
                [[fallthrough]];
            default:
                LOG_DEBUG("ServiceDesktop::SetTransferTimerState unhandled req:%u", timerStateMsg->req);
                break;
            }
        }
        return sys::MessageNone{};
    });

    connect(sdesktop::usb::USBConfigured(), [&](sys::Message *msg) {
        auto message = static_cast<sdesktop::usb::USBConfigured *>(msg);
        if (message->getConfigurationType() == sdesktop::usb::USBConfigurationType::firstConfiguration) {


@@ 299,8 234,6 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        auto msgl = static_cast<message::bluetooth::ResponseVisibleDevices *>(msg);
        return btMsgHandler->handle(msgl);
    });
    settings->registerValueChange(updateos::settings::history,
                                  [this](const std::string &value) { updateOS->setInitialHistory(value); });

    return (sys::ReturnCodes::Success);
}


@@ 348,11 281,6 @@ sys::MessagePointer ServiceDesktop::DataReceivedHandler(sys::DataMessage *msg, s
    return std::make_shared<sys::ResponseMessage>();
}

void ServiceDesktop::storeHistory(const std::string &historyValue)
{
    settings->setValue(updateos::settings::history, historyValue);
}

void ServiceDesktop::prepareBackupData()
{
    backupRestoreStatus.operation = ServiceDesktop::Operation::Backup;

M module-services/service-desktop/WorkerDesktop.cpp => module-services/service-desktop/WorkerDesktop.cpp +7 -177
@@ 25,8 25,8 @@ inline constexpr auto uploadFailedMessage = "file upload terminated before all d
WorkerDesktop::WorkerDesktop(sys::Service *ownerServicePtr,
                             const sdesktop::USBSecurityModel &securityModel,
                             const std::string serialNumber)
    : sys::Worker(ownerServicePtr, sdesktop::worker_stack), fileDes(nullptr), securityModel(securityModel),
      serialNumber(serialNumber), ownerService(ownerServicePtr), parser(ownerServicePtr)
    : sys::Worker(ownerServicePtr, sdesktop::worker_stack), securityModel(securityModel), serialNumber(serialNumber),
      ownerService(ownerServicePtr), parser(ownerServicePtr)
{}

bool WorkerDesktop::init(std::list<sys::WorkerQueueInfo> queues)


@@ 44,7 44,7 @@ bool WorkerDesktop::init(std::list<sys::WorkerQueueInfo> queues)
    usbSuspendTimer = sys::TimerFactory::createSingleShotTimer(
        ownerService, "usbSuspend", constants::usbSuspendTimeout, [this](sys::Timer &) { suspendUsb(); });

    bsp::usbInitParams initParams = {receiveQueue, irqQueue, this, serialNumber.c_str()};
    bsp::usbInitParams initParams = {receiveQueue, irqQueue, serialNumber.c_str()};

    return (bsp::usbInit(initParams) < 0) ? false : true;
}


@@ 60,11 60,6 @@ bool WorkerDesktop::deinit(void)
    /// additional wait to flush on serial - we should wait for data sent...
    vTaskDelay(3000);

    if (fileDes != nullptr) {
        LOG_DEBUG("deinit close opened fileDes");
        fclose(fileDes);
    }

    bsp::usbDeinit();

    Worker::deinit();


@@ 118,11 113,10 @@ bool WorkerDesktop::handleMessage(uint32_t queueID)
        sys::WorkerCommand cmd;

        if (serviceQueue.Dequeue(&cmd, 0)) {
            switch (static_cast<Command>(cmd.command)) {
            case Command::CancelTransfer:
                transferTimeoutHandler();
                break;
            }
            LOG_DEBUG("Received cmd: %d", static_cast<int>(cmd.command));
#if defined(DEBUG)
            assert(false);
#endif
        }
        else {
            LOG_ERROR("handleMessage xQueueReceive failed for %s.", SERVICE_QUEUE_NAME.c_str());


@@ 171,170 165,6 @@ bool WorkerDesktop::handleMessage(uint32_t queueID)
    return true;
}

sys::ReturnCodes WorkerDesktop::startDownload(const std::filesystem::path &destinationPath,
                                              uint32_t fileSize,
                                              std::string fileCrc32)
{
    filePath = destinationPath;
    fileDes  = fopen(filePath.c_str(), "w");

    if (fileDes == nullptr)
        return sys::ReturnCodes::Failure;

    if (fileSize <= 0)
        return sys::ReturnCodes::Failure;

    startTransferTimer();

    writeFileSizeExpected = fileSize;
    expectedFileCrc32     = fileCrc32;
    rawModeEnabled        = true;

    digestCrc32.reset();

    LOG_DEBUG("startDownload all checks passed starting download");
    return sys::ReturnCodes::Success;
}

void WorkerDesktop::startTransferTimer()
{
    auto msg = std::make_shared<sdesktop::transfer::TransferTimerState>(
        sdesktop::transfer::TransferTimerState::Request::Start);
    ownerService->bus.sendUnicast(std::move(msg), service::name::service_desktop);
}

void WorkerDesktop::stopTransferTimer()
{
    auto msg =
        std::make_shared<sdesktop::transfer::TransferTimerState>(sdesktop::transfer::TransferTimerState::Request::Stop);
    ownerService->bus.sendUnicast(std::move(msg), service::name::service_desktop);
}
void WorkerDesktop::reloadTransferTimer()
{
    auto msg = std::make_shared<sdesktop::transfer::TransferTimerState>(
        sdesktop::transfer::TransferTimerState::Request::Reload);
    ownerService->bus.sendUnicast(std::move(msg), service::name::service_desktop);
}

void WorkerDesktop::stopTransfer(const TransferFailAction action)
{
    parser.setState(parserFSM::State::NoMsg);
    rawModeEnabled = false;

    auto removeFile     = (action == TransferFailAction::removeDesitnationFile);
    auto responseStatus = removeFile ? parserFSM::http::Code::NotAcceptable : parserFSM::http::Code::Accepted;

    const auto fileCrc32  = digestCrc32.getHash();
    const auto crc32Match = expectedFileCrc32.empty() ? true : (expectedFileCrc32.compare(fileCrc32) == 0);

    if (!crc32Match && !removeFile) {
        responseStatus = parserFSM::http::Code::NotAcceptable;
        removeFile     = true;

        LOG_ERROR(
            "File transfer CRC32 mismatch, expected: %s, actual: %s", expectedFileCrc32.c_str(), fileCrc32.c_str());
    }

    parserFSM::Context responseContext;
    responseContext.setResponseStatus(responseStatus);
    responseContext.setEndpoint(parserFSM::EndpointType::filesystemUpload);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::fileSize, std::to_string(writeFileDataWritten)},
                                                     {parserFSM::json::fileName, filePath.filename().c_str()},
                                                     {parserFSM::json::fileCrc32, fileCrc32.c_str()}};
    responseContext.setResponseBody(responseJson);

    // close the file descriptor
    std::fclose(fileDes);

    // stop the timeout timer
    stopTransferTimer();

    // reset all counters
    writeFileSizeExpected = 0;
    writeFileDataWritten  = 0;

    if (removeFile) {
        try {
            if (!std::filesystem::remove(filePath)) {
                LOG_ERROR("can't delete file");
            }
            LOG_DEBUG("Deleted file");
        }
        catch (const std::filesystem::filesystem_error &fsError) {
            LOG_ERROR("Removing file failed");
        }
    }

    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

void WorkerDesktop::rawDataReceived(void *dataPtr, uint32_t dataLen)
{
    if (getRawMode()) {
        reloadTransferTimer();

        if (dataPtr == nullptr || dataLen == 0) {
            LOG_ERROR("transferDataReceived invalid data");
            return;
        }

        digestCrc32.add(dataPtr, dataLen);

        const uint32_t bytesWritten = std::fwrite(dataPtr, 1, dataLen, fileDes);

        if (bytesWritten != dataLen) {
            LOG_ERROR("transferDataReceived write failed bytesWritten=%" PRIu32 " != dataLen=%" PRIu32,
                      bytesWritten,
                      dataLen);
            return;
        }

        writeFileDataWritten += dataLen;

        if (writeFileDataWritten >= writeFileSizeExpected) {
            LOG_INFO("transferDataReceived all data transferred, stop now");
            stopTransfer(TransferFailAction::doNothing);
        }
    }
    else {
        LOG_DEBUG("transferDataReceived not in a transfer state");
    }
}

void WorkerDesktop::cancelTransferOnTimeout()
{
    LOG_DEBUG("timeout timer: run");
    sendCommand({.command = static_cast<uint32_t>(Command::CancelTransfer), .data = nullptr});
}

void WorkerDesktop::transferTimeoutHandler()
{
    if (getRawMode()) {
        LOG_DEBUG("timeout timer: stopping transfer");
        uploadFileFailedResponse();
        stopTransfer(TransferFailAction::removeDesitnationFile);
    }
}

bool WorkerDesktop::getRawMode() const noexcept
{
    return rawModeEnabled;
}

void WorkerDesktop::uploadFileFailedResponse()
{
    LOG_ERROR("Upload file failed, timeout");
    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::InternalServerError);
    responseContext.setEndpoint(parserFSM::EndpointType::filesystemUpload);

    json11::Json responseJson = json11::Json::object{
        {parserFSM::json::status, uploadFailedMessage},
    };
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());
}

void WorkerDesktop::suspendUsb()
{
    bsp::usbSuspend();

M module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.cpp +0 -3
@@ 37,8 37,5 @@ auto DeveloperModeEndpoint::helperSwitcher(parserFSM::Context &ctx) -> parserFSM
    if (ctx.getBody()["ui"] == true) {
        return *uiHelper;
    }
    if (ctx.getBody()["update"] == true) {
        return *updateHelper;
    }
    return *helper;
}

M module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.hpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeEndpoint.hpp +1 -4
@@ 5,7 5,6 @@

#include "DeveloperModeHelper.hpp"
#include "Mode/UI_Helper.hpp"
#include "Mode/UpdateHelper.hpp"
#include <endpoints/Endpoint.hpp>
#include <parser/ParserUtils.hpp>



@@ 28,13 27,11 @@ class DeveloperModeEndpoint : public parserFSM::Endpoint
  private:
    const std::unique_ptr<parserFSM::DeveloperModeHelper> helper;
    const std::unique_ptr<parserFSM::UI_Helper> uiHelper;
    const std::unique_ptr<parserFSM::UpdateHelper> updateHelper;

  public:
    explicit DeveloperModeEndpoint(sys::Service *_ownerServicePtr)
        : Endpoint(_ownerServicePtr), helper(std::make_unique<parserFSM::DeveloperModeHelper>(ownerServicePtr)),
          uiHelper(std::make_unique<parserFSM::UI_Helper>(ownerServicePtr)),
          updateHelper(std::make_unique<parserFSM::UpdateHelper>(ownerServicePtr))
          uiHelper(std::make_unique<parserFSM::UI_Helper>(ownerServicePtr))
    {
        debugName = "DeveloperModeEndpoint";
    }

M module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp => module-services/service-desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp +0 -3
@@ 3,7 3,6 @@

#include "DeviceInfoEndpoint.hpp"
#include <endpoints/Context.hpp>
#include <endpoints/update/UpdateMuditaOS.hpp>
#include <parser/MessageHandler.hpp>

#include <EventStore.hpp>


@@ 43,7 42,6 @@ auto DeviceInfoEndpoint::getDeviceInfo(Context &context) -> bool
    if (ownerServicePtr == nullptr) {
        return false;
    }
    json11::Json updateHistory   = static_cast<ServiceDesktop *>(ownerServicePtr)->updateOS->getUpdateHistory();
    std::unique_ptr<struct statvfs> vfstat = std::make_unique<struct statvfs>();
    if ((*statvfs)(purefs::dir::getRootDiskPath().c_str(), vfstat.get()) < 0) {
        return false;


@@ 68,7 66,6 @@ auto DeviceInfoEndpoint::getDeviceInfo(Context &context) -> bool
         {json::gitRevision, (std::string)(GIT_REV)},
         {json::gitTag, (std::string)GIT_TAG},
         {json::gitBranch, (std::string)GIT_BRANCH},
         {json::updateHistory, updateHistory},
         {json::currentRTCTime, std::to_string(static_cast<uint32_t>(std::time(nullptr)))},
         {json::version, std::string(VERSION)},
         {json::serialNumber, getSerialNumber()}}));

M module-services/service-desktop/endpoints/filesystem/FileOperations.hpp => module-services/service-desktop/endpoints/filesystem/FileOperations.hpp +4 -2
@@ 48,8 48,10 @@ class FileOperations
    static constexpr auto BinToBase64Factor = 4u;
    static constexpr auto Base64ToBinFactor = 3u;
    static constexpr auto Mod3MaxReminder   = 2u;
    // ChunkSize must be a multiple of 3 and 4 so that ChunkSize % 12 == 0
    static constexpr auto ChunkSize = Base64ToBinFactor * BinToBase64Factor * 1024u;
    // SingleChunkSize must be a multiple of 3 and 4 so that SingleChunkSize % 12 == 0
    static constexpr auto SingleChunkSize     = Base64ToBinFactor * BinToBase64Factor * 1024u; // 12KB
    static constexpr auto ChunkSizeMultiplier = 12u;
    static constexpr auto ChunkSize           = ChunkSizeMultiplier * SingleChunkSize;

    static FileOperations &instance();


M module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp => module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp +4 -32
@@ 5,9 5,12 @@
#include "FileOperations.hpp"
#include "service-desktop/DesktopMessages.hpp"
#include "service-desktop/ServiceDesktop.hpp"
#include <purefs/filesystem_paths.hpp>

#include <filesystem>

using namespace parserFSM;
namespace fs = std::filesystem;

auto FilesystemEndpoint::handle(Context &context) -> void
{


@@ 29,14 32,6 @@ auto FilesystemEndpoint::handle(Context &context) -> void
    }
    LOG_DEBUG("returnCode: %u", static_cast<unsigned>(returnCode));
}
static bool isWritable(const fs::path &file)
{
    auto lamb = [](std::FILE *stream) { std::fclose(stream); };

    std::unique_ptr<std::FILE, decltype(lamb)> sf(std::fopen(file.c_str(), "w"), lamb);

    return static_cast<bool>(sf);
}

auto FilesystemEndpoint::requestLogsFlush() const -> void
{


@@ 170,30 165,7 @@ auto FilesystemEndpoint::runPost(Context &context) -> sys::ReturnCodes
    context.setResponseBody(
        json11::Json::object({{json::status, std::to_string(static_cast<int>(sys::ReturnCodes::Failure))}}));

    auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);

    if (cmd == parserFSM::json::filesystem::commands::download) {
        fs::path filePath    = context.getBody()[parserFSM::json::fileName].string_value();
        fs::path tmpFilePath = purefs::dir::getUpdatesOSPath() / filePath;

        const uint32_t fileSize = context.getBody()[parserFSM::json::fileSize].int_value();
        const auto fileCrc32    = context.getBody()[parserFSM::json::fileCrc32].string_value();

        LOG_DEBUG("got owner for tmp file");

        if (isWritable(tmpFilePath)) {
            LOG_INFO("download %" PRIu32 " bytes to tmp file", fileSize);

            if (owner->desktopWorker->startDownload(tmpFilePath, fileSize, fileCrc32) == sys::ReturnCodes::Success) {
                context.setResponseStatus(parserFSM::http::Code::Accepted);
                returnCode = sys::ReturnCodes::Success;
            }
        }
        else {
            LOG_ERROR("download command failed, can't write %" PRIu32 " bytes to tmp file", fileSize);
        }
    }
    else if (cmd == parserFSM::json::filesystem::commands::checkFile) {
    if (cmd == parserFSM::json::filesystem::commands::checkFile) {
        fs::path filePath = context.getBody()[parserFSM::json::fileName].string_value();
        LOG_DEBUG("Checking file");


M module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp => module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp +1 -0
@@ 9,6 9,7 @@
#include <service-desktop/DesktopMessages.hpp>
#include <service-desktop/ServiceDesktop.hpp>
#include <service-desktop/endpoints/backup/BackupRestore.hpp>
#include <purefs/filesystem_paths.hpp>

#include <memory>


M module-services/service-desktop/endpoints/update/UpdateEndpoint.cpp => module-services/service-desktop/endpoints/update/UpdateEndpoint.cpp +13 -76
@@ 2,7 2,6 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "UpdateEndpoint.hpp"
#include "UpdateMuditaOS.hpp"

#include <service-desktop/DesktopMessages.hpp>
#include <service-desktop/ServiceDesktop.hpp>


@@ 10,97 9,35 @@
#include <endpoints/messages/MessageHelper.hpp>

#include <json11.hpp>
#include <purefs/filesystem_paths.hpp>

#include <filesystem>
#include <memory>

using namespace parserFSM;

auto UpdateEndpoint::handle(Context &context) -> void
{
    switch (context.getMethod()) {
    case http::Method::post:
        run(context);
        break;
    case http::Method::get:
        getUpdates(context);
        break;
    default:
        break;
    auto &p               = helperSwitcher(context);
    auto [sent, response] = p.process(context.getMethod(), context);
    if (sent == sent::delayed) {
        LOG_DEBUG("There is no proper delayed serving mechanism - depend on invisible context caching");
    }
}

auto UpdateEndpoint::run(Context &context) -> sys::ReturnCodes
{
    std::string cmd = context.getBody()[parserFSM::json::updateprocess::command].string_value();
    if (cmd == parserFSM::json::updateprocess::commands::abort) {
        auto owner        = static_cast<ServiceDesktop *>(ownerServicePtr);
        auto currentState = owner->updateOS->status;
        if (currentState <= updateos::UpdateState::ExtractingFiles) {
            owner->updateOS->setUpdateAbortFlag(true);
            context.setResponseBody(json11::Json::object({{parserFSM::json::updateprocess::updateAborted, true}}));
            context.setResponseStatus(http::Code::OK);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Success;
    if (sent == sent::no) {
        if (not response) {
            LOG_ERROR("Response not sent & response not created : respond with error");
            context.setResponseStatus(http::Code::NotAcceptable);
        }
        else {
            context.setResponseBody(json11::Json::object({{parserFSM::json::updateprocess::updateAborted, false}}));
            context.setResponseStatus(http::Code::NotAcceptable);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Failure;
            context.setResponse(response.value());
        }
    }

    std::string fileName = context.getBody()["fileName"].string_value();
    auto path            = purefs::dir::getUpdatesOSPath() / fileName;
    auto fileExists      = std::filesystem::exists(path.c_str());
    if (fileExists) {
        context.setResponseBody(json11::Json::object({{parserFSM::json::updateReady, true}}));

        auto msg = std::make_shared<sdesktop::UpdateOsMessage>(fileName, context.getUuid());
        ownerServicePtr->bus.sendUnicast(msg, service::name::service_desktop);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Success;
    }
    else {
        context.setResponseBody(json11::Json::object({{parserFSM::json::updateReady, false}}));
        context.setResponseStatus(http::Code::InternalServerError);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Failure;
    if (sent == sent::yes and response) {
        LOG_ERROR("Response set when we already handled response in handler");
    }
}

auto UpdateEndpoint::getUpdates(Context &context) -> sys::ReturnCodes
auto UpdateEndpoint::helperSwitcher(parserFSM::Context &ctx) -> parserFSM::BaseHelper &
{
    const auto updatesOSPath = purefs::dir::getUpdatesOSPath();
    std::error_code errorCode;
    struct DirectoryEntry
    {
        std::string fileName;
        uint32_t fileSize;
        json11::Json to_json() const
        {
            return (json11::Json::object{{"name", fileName}, {"size", std::to_string(fileSize)}});
        }
    };

    auto dirEntryVector = std::vector<DirectoryEntry>();
    for (const auto &p : fs::directory_iterator(updatesOSPath, errorCode)) {
        if (errorCode) {
            LOG_WARN("can't get directory contents for %s, \"%s\"", updatesOSPath.c_str(), errorCode.message().c_str());
            return sys::ReturnCodes::Failure;
        }

        if (!p.is_directory() && p.path().extension() == updateos::extension::update) {
            dirEntryVector.push_back(DirectoryEntry{p.path().string(), static_cast<uint32_t>(p.file_size())});
        }
    }

    json11::Json fileList = dirEntryVector;
    context.setResponseBody(json11::Json::object{{parserFSM::json::updateFileList, fileList}});

    MessageHandler::putToSendQueue(context.createSimpleResponse());

    return sys::ReturnCodes::Success;
    return *updateHelper;
}

M module-services/service-desktop/endpoints/update/UpdateEndpoint.hpp => module-services/service-desktop/endpoints/update/UpdateEndpoint.hpp +7 -3
@@ 3,6 3,7 @@

#pragma once

#include "UpdateHelper.hpp"
#include <endpoints/Endpoint.hpp>
#include <parser/ParserUtils.hpp>



@@ 22,13 23,16 @@ namespace sys

class UpdateEndpoint : public parserFSM::Endpoint
{
  private:
    const std::unique_ptr<parserFSM::UpdateHelper> updateHelper;

  public:
    explicit UpdateEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
    explicit UpdateEndpoint(sys::Service *ownerServicePtr)
        : Endpoint(ownerServicePtr), updateHelper(std::make_unique<parserFSM::UpdateHelper>(ownerServicePtr))
    {
        debugName = "UpdateEndpoint";
    }
    auto handle(parserFSM::Context &context) -> void override;
    auto run(parserFSM::Context &context) -> sys::ReturnCodes;
    auto getUpdates(parserFSM::Context &context) -> sys::ReturnCodes;

    [[nodiscard]] auto helperSwitcher(parserFSM::Context &ctx) -> parserFSM::BaseHelper &;
};

R module-services/service-desktop/endpoints/developerMode/Mode/UpdateHelper.cpp => module-services/service-desktop/endpoints/update/UpdateHelper.cpp +18 -8
@@ 4,9 4,14 @@
#include "UpdateHelper.hpp"
#include <log.hpp>
#include <SystemManager/SystemManager.hpp>
#include <purefs/filesystem_paths.hpp>

#include <filesystem>

namespace parserFSM
{
    auto constexpr updatePackageFile = "update.tar";

    void UpdateHelper::preProcess(http::Method method, Context &context)
    {
        LOG_INFO("In UpdateHelper - requesting %d", static_cast<int>(method));


@@ 15,14 20,19 @@ namespace parserFSM
    auto UpdateHelper::processPost(Context &context) -> ProcessResult
    {
        const auto &body = context.getBody();
        if (body["reboot"] == true) {
            if (sys::SystemManager::RebootToUpdate(owner, sys::UpdateReason::Update)) {
                return {sent::no, endpoint::ResponseContext{.status = http::Code::OK}};
            }
            else {
                return {sent::no, endpoint::ResponseContext{.status = http::Code::InternalServerError}};
            }

        if (!(body["update"] == true && body["reboot"] == true)) {
            return {sent::no, endpoint::ResponseContext{.status = http::Code::BadRequest}};
        }

        if (!std::filesystem::exists(purefs::dir::getUserDiskPath() / updatePackageFile)) {
            return {sent::no, endpoint::ResponseContext{.status = http::Code::NotFound}};
        }

        if (sys::SystemManager::RebootToUpdate(owner, sys::UpdateReason::Update)) {
            return {sent::no, endpoint::ResponseContext{.status = http::Code::NoContent}};
        }
        return {sent::no, std::nullopt};

        return {sent::no, endpoint::ResponseContext{.status = http::Code::InternalServerError}};
    }
} // namespace parserFSM

R module-services/service-desktop/endpoints/developerMode/Mode/UpdateHelper.hpp => module-services/service-desktop/endpoints/update/UpdateHelper.hpp +0 -1
@@ 12,7 12,6 @@ namespace parserFSM

    class UpdateHelper : public BaseHelper
    {

      public:
        explicit UpdateHelper(sys::Service *p) : BaseHelper(p)
        {}

D module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp +0 -924
@@ 1,924 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "UpdateMuditaOS.hpp"
#include <service-desktop/ServiceDesktop.hpp>
#include <service-desktop/DesktopMessages.hpp>
#include <SystemManager/SystemManager.hpp>
#include <crc32.h>
#include <json11.hpp>
#include <log/log.hpp>
#include <application-desktop/Constants.hpp>
#include <service-db/service-db/Settings.hpp>
#include <purefs/filesystem_paths.hpp>
#include <filesystem>
#include <Utils.hpp>
#include <boot/bootconfig.hpp>
#include <boot/bootconstants.hpp>

#if defined(TARGET_RT1051)
#include <board/cross/eMMC/eMMC.hpp>
#include "bsp/watchdog/watchdog.hpp"
#endif

#include <array>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <memory>

FileInfo::FileInfo(mtar_header_t &h, unsigned long crc32) : fileSize(h.size), fileCRC32(crc32)
{
    if (h.name[0] == '.' && h.name[1] == '/') {
        // microtar relative paths, strip the leading ./away
        fileName = std::string(h.name).substr(2);
    }
    else {
        fileName = std::string(h.name);
    }
}

json11::Json FileInfo::to_json() const
{
    return json11::Json::object{
        {"name", fileName}, {"size", std::to_string(fileSize)}, {"crc32", std::to_string(fileCRC32)}};
}

UpdateMuditaOS::UpdateMuditaOS(ServiceDesktop *ownerService) : owner(ownerService)
{
    status = updateos::UpdateState::Initial;
    bootConfig.load();
}

updateos::UpdateError UpdateMuditaOS::setUpdateFile(const std::filesystem::path &updatesOSPath,
                                                    const fs::path &updateFileToUse)
{
    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return informError(updateos::UpdateError::UpdateAborted, "update aborted");
    }
    else {
        informUpdateWindow();
    }

    updateFile = purefs::dir::getUpdatesOSPath() / updateFileToUse;
    if (std::filesystem::exists(updateFile.c_str())) {
        versionInformation = UpdateMuditaOS::getVersionInfoFromFile(updateFile);
        if (mtar_open(&updateTar, updateFile.c_str(), "r") == MTAR_ESUCCESS) {
            totalBytes = std::filesystem::file_size(updateFile);
        }
        else {
            return informError(updateos::UpdateError::CantOpenUpdateFile,
                               "UpdateMuditaOS::setUpdateFile can't open TAR file %s",
                               updateFile.c_str());
        }
    }
    else {
        return informError(updateos::UpdateError::CantOpenUpdateFile,
                           "UpdateMuditaOS::setUpdateFile %s does not exist",
                           updateFile.c_str());
    }

    status = updateos::UpdateState::UpdateFileSet;

    informUpdateWindow();

    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::runUpdate()
{
    informDebug("Preparing temp dir");

    updateRunStatus.startTime   = static_cast<uint32_t>(std::time(nullptr));
    updateRunStatus.fromVersion = bootConfig.to_json()[boot::json::git_info];
    versionInformation          = UpdateMuditaOS::getVersionInfoFromFile(updateFile);

    auto currentOSVersion = bootConfig.os_version();
    auto updateOSVersion  = versionInformation[boot::json::os_version][boot::json::version_string].string_value();

    storeRunStatusInDB();

    updateos::UpdateError err =
        prepareTempDirForUpdate(purefs::dir::getTemporaryPath(), purefs::dir::getUpdatesOSPath());
    if (err != updateos::UpdateError::NoError) {
        if (err == updateos::UpdateError::UpdateAborted) {
            return informError(updateos::UpdateError::UpdateAborted, "update aborted");
        }
        else {
            return informError(err, "runUpdate can't prepare temp directory for update");
        }
    }

    informDebug("Unpacking update");
    err = unpackUpdate();
    if (err == updateos::UpdateError::NoError) {
        informUpdate(status, "Unpacked");
    }
    else if (err == updateos::UpdateError::UpdateAborted) {
        return informError(updateos::UpdateError::UpdateAborted, "update aborted");
    }
    else {
        return informError(err, "%s can't be unpacked", updateFile.c_str());
    }

    err = verifyChecksums();
    if (err == updateos::UpdateError::NoError) {
        informUpdate(status, "Verify checksums");
    }
    else if (err == updateos::UpdateError::UpdateAborted) {
        return informError(updateos::UpdateError::UpdateAborted, "update aborted");
    }
    else {
        return informError(err, "Checksum verification failed");
    }

    if ((err = verifyVersion()) == updateos::UpdateError::NoError) {
        informUpdate(status, "Verify version");
    }
    else {
        return informError(err, "Can't verify version");
    }

    // at this point we should set the system to update mode we are
    // writing directly to eMMC when updating the bootloader
    // then placing the new files in destination folders/files
    sys::SystemManager::Update(owner, updateOSVersion, currentOSVersion);

    if ((err = updateBootloader()) == updateos::UpdateError::NoError) {
        informUpdate(status, "Update bootloader");
    }
    else {
        return informError(err, "Failed to update the bootloader");
    }

    if ((err = prepareRoot()) == updateos::UpdateError::NoError) {
        informUpdate(status, "Ready for reset");
    }
    else {
        informError(err, "Can't prepare root dir for reset");
    }

    if ((err = cleanupAfterUpdate()) != updateos::UpdateError::NoError) {
        informError(err, "runUpdate cleanupAfterUpdate failed, resetting anyway");
    }

    updateRunStatus.endTime = static_cast<uint32_t>(std::time(nullptr));
    storeRunStatusInDB();

    // reboot always
    sys::SystemManager::Reboot(owner);

    sys::SystemManager::storeOsVersion(owner, updateOSVersion, updateOSVersion);

    return err;
}

updateos::UpdateError UpdateMuditaOS::unpackUpdate()
{
    status = updateos::UpdateState::ExtractingFiles;

    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return updateos::UpdateError::UpdateAborted;
    }
    else {
        informUpdateWindow();
    }

    mtar_header_t tarHeader;
    filesInUpdatePackage.clear();

    std::rewind(updateTar.stream);
    while ((mtar_read_header(&updateTar, &tarHeader)) != MTAR_ENULLRECORD) {
        if (isUpdateToBeAborted()) {
            setUpdateAbortFlag(false);
            sys::SystemManager::Reboot(owner);
            return updateos::UpdateError::UpdateAborted;
        }
        if (std::string(tarHeader.name) == "./") {
            mtar_next(&updateTar);
            continue;
        }
        unsigned long fileCRC32 = 0;
        if (tarHeader.type == MTAR_TDIR) {
            fs::path tmpPath = getUpdateTmpChild(tarHeader.name);
            if (!std::filesystem::create_directory(tmpPath.c_str())) {
                return informError(updateos::UpdateError::CantCreateExtractedFile,
                                   "unpackUpdate failed to create %s when extracting update tar",
                                   tmpPath.c_str());
            }
        }
        else {
            if (unpackFileToTemp(tarHeader, &fileCRC32) == false) {
                return informError(updateos::UpdateError::CantCreateExtractedFile,
                                   "unpackUpdate failed to extract update file %s",
                                   tarHeader.name);
            }
            filesInUpdatePackage.emplace_back(FileInfo(tarHeader, fileCRC32));
        }

        mtar_next(&updateTar);
    }

    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::verifyChecksums()
{
    LOG_DEBUG("Checksum Verify");
    status = updateos::UpdateState::ChecksumVerification;

    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return updateos::UpdateError::UpdateAborted;
    }
    else {
        informUpdateWindow();
    }

    fs::path checksumsFile = getUpdateTmpChild(updateos::file::checksums);
    std::ifstream fpChecksums(checksumsFile.string(), std::ios::binary);

    if (!fpChecksums.is_open()) {
        return informError(updateos::UpdateError::CantOpenChecksumsFile,
                           "verifyChecksums can't open checksums file %s",
                           checksumsFile.c_str());
    }

    while (!fpChecksums.eof()) {
        std::string line;
        std::getline(fpChecksums, line);

        std::string filePath;
        unsigned long fileCRC32;

        if (line[0] == ';') {
            continue;
        }

        getChecksumInfo(line, filePath, &fileCRC32);
        unsigned long computedCRC32 = getExtractedFileCRC32(filePath);
        if (computedCRC32 != fileCRC32) {
            fpChecksums.close();
            return informError(updateos::UpdateError::VerifyChecksumsFailure,
                               "verifyChecksums %s crc32 match FAIL %lX != %lX",
                               filePath.c_str(),
                               fileCRC32,
                               computedCRC32);
        }
    }
    fpChecksums.close();
    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::verifyVersion()
{
    status = updateos::UpdateState::VersionVerificiation;

    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return updateos::UpdateError::UpdateAborted;
    }
    else {
        informUpdateWindow();
    }

    if (!std::filesystem::exists(getUpdateTmpChild(updateos::file::version).c_str())) {
        return informError(updateos::UpdateError::VerifyVersionFailure,
                           "verifyVersion %s does not exist",
                           getUpdateTmpChild(updateos::file::version).c_str());
    }

    std::ifstream version_file(getUpdateTmpChild(updateos::file::version).string(), std::ios::binary);
    std::string versionJsonString(std::istreambuf_iterator<char>(version_file), (std::istreambuf_iterator<char>()));
    std::string parserError;
    targetVersionInfo = json11::Json::parse(versionJsonString, parserError);
    if (!parserError.empty()) {
        return informError(
            updateos::UpdateError::VerifyVersionFailure, "verifyVersion parse json error: %s", parserError.c_str());
    }
    else {
        /* version comparison goes here */
        updateRunStatus.toVersion = targetVersionInfo[boot::json::git_info];
        const bool ret = bootConfig.version_compare(targetVersionInfo[boot::json::version_string].string_value(),
                                                    bootConfig.os_version());
        LOG_DEBUG("verifyVersion comparison result == %s", ret ? "true" : "false");
    }
    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::updateBootloader()
{
    informDebug("updateBootloader");
    status = updateos::UpdateState::UpdatingBootloader;

    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return updateos::UpdateError::UpdateAborted;
    }
    else {
        informUpdateWindow();
    }

    if (targetVersionInfo[boot::json::bootloader][parserFSM::json::fileName].is_string()) {
        fs::path bootloaderFile =
            getUpdateTmpChild(targetVersionInfo[boot::json::bootloader][parserFSM::json::fileName].string_value());
        return writeBootloader(bootloaderFile);
    }
    return updateos::UpdateError::NoError;
}

unsigned long UpdateMuditaOS::getExtractedFileCRC32(const std::string &filePath)
{
    for (const auto &file : filesInUpdatePackage) {
        if (file.fileName == filePath) {
            return file.fileCRC32;
        }
    }
    return 0;
}

void UpdateMuditaOS::getChecksumInfo(const std::string &infoLine, std::string &filePath, unsigned long *fileCRC32Long)
{
    std::size_t lastSpacePos = infoLine.find_last_of(' ');
    if (lastSpacePos > 0) {
        filePath                       = infoLine.substr(0, lastSpacePos);
        const std::string fileCRC32Str = infoLine.substr(lastSpacePos + 1, boot::consts::crc_char_size);
        if (fileCRC32Long != nullptr) {
            *fileCRC32Long = strtoull(fileCRC32Str.c_str(), nullptr, boot::consts::crc_radix);
            informDebug("getChecksumInfo filePath: %s fileCRC32Str: %s fileCRC32Long: %lu fileCRC32Hex: %lX",
                        filePath.c_str(),
                        fileCRC32Str.c_str(),
                        *fileCRC32Long,
                        *fileCRC32Long);
        }
    }
}

updateos::UpdateError UpdateMuditaOS::prepareRoot()
{
    informDebug("prepareRoot()");
    // basic needed dirs

    status                    = updateos::UpdateState::VersionVerificiation;
    const auto previousOSPath = purefs::dir::getPreviousOSPath();
    const auto currentOSPath  = purefs::dir::getCurrentOSPath();
    const auto userDiskPath   = purefs::dir::getUserDiskPath();

    informDebug("prepareRoot mkdir: %s", previousOSPath.c_str());
    std::filesystem::create_directory(previousOSPath.c_str());
    informDebug("prepareRoot mkdir: %s", currentOSPath.c_str());
    std::filesystem::create_directory(currentOSPath.c_str());
    informDebug("prepareRoot mkdir: %s", userDiskPath.c_str());
    std::filesystem::create_directory(userDiskPath.c_str());

    // remove the previous OS version from partition
    informDebug("prepareRoot deltree: %s", previousOSPath.c_str());
    try {
        std::filesystem::remove_all(previousOSPath.c_str());
    }
    catch (const std::filesystem::filesystem_error &fsError) {
        return informError(updateos::UpdateError::CantDeletePreviousOS,
                           "prepareRoot remove_all on %s caused an error %s",
                           previousOSPath.c_str(),
                           fsError.what());
    }

    if (std::filesystem::is_directory(purefs::dir::getPreviousOSPath().c_str())) {
        return informError(
            updateos::UpdateError::CantDeletePreviousOS, "prepareRoot ff_deltree on %s", previousOSPath.c_str());
    }

    if (std::filesystem::is_directory(purefs::dir::getPreviousOSPath().c_str())) {
        return informError(updateos::UpdateError::CantDeletePreviousOS,
                           "prepareRoot %s still exists, we can't continue",
                           purefs::dir::getPreviousOSPath().c_str());
    }
    // rename the current OS to previous on partition
    informDebug("prepareRoot rename: %s->%s", currentOSPath.c_str(), previousOSPath.c_str());
    try {
        std::filesystem::rename(currentOSPath.c_str(), previousOSPath.c_str());
    }
    catch (const std::filesystem::filesystem_error &fsError) {
        return informError(updateos::UpdateError::CantRenameCurrentToPrevious,
                           "prepareRoot can't rename %s -> %s error %s",
                           purefs::dir::getCurrentOSPath().c_str(),
                           purefs::dir::getPreviousOSPath().c_str(),
                           fsError.what());
    }

    // rename the temp directory to current (extracted update)
    informDebug("prepareRoot copy: %s->%s", updateTempDirectory.c_str(), currentOSPath.c_str());
    try {
        std::filesystem::copy(
            updateTempDirectory.c_str(), currentOSPath.c_str(), std::filesystem::copy_options::recursive);
    }
    catch (const std::filesystem::filesystem_error &fsError) {
        return informError(updateos::UpdateError::CantCopyTempToCurrent,
                           "prepareRoot can't copy %s -> %s error %s",
                           updateTempDirectory.c_str(),
                           purefs::dir::getCurrentOSPath().c_str(),
                           fsError.what());
    }

    // move the contents of /sys/current/user if it exists to /user
    updateUserData();

    return updateBootJSON();
}

updateos::UpdateError UpdateMuditaOS::updateBootJSON()
{
    fs::path bootJSONAbsoulte = purefs::createPath(purefs::dir::getRootDiskPath(), purefs::file::boot_json);
    informDebug("updateBootJSON %s", bootJSONAbsoulte.c_str());

    auto *fp = std::fopen(bootJSONAbsoulte.c_str(), "r");

    if (fp != nullptr) {
        unsigned long bootJSONAbsoulteCRC = utils::filesystem::computeFileCRC32(fp);
        bootJSONAbsoulte += boot::consts::ext_crc32;

        auto *fpCRC = std::fopen(bootJSONAbsoulte.c_str(), "w");
        if (fpCRC != nullptr) {
            std::array<char, boot::consts::crc_char_size + 1> crcBuf{};
            snprintf(crcBuf.data(), crcBuf.size(), "%lX", bootJSONAbsoulteCRC);
            std::fwrite(crcBuf.data(), 1, boot::consts::crc_char_size, fpCRC);
            std::fclose(fpCRC);
        }
        else {
            return informError(updateos::UpdateError::CantUpdateCRC32JSON,
                               "Can't open %s for writing",
                               (bootJSONAbsoulte += boot::consts::ext_crc32).c_str());
        }

        std::fclose(fp);
    }
    else {
        return informError(
            updateos::UpdateError::CantUpdateCRC32JSON, "Can't open %s for reading", bootJSONAbsoulte.c_str());
    }

    informDebug("updateBootJSON no error");
    return updateos::UpdateError::NoError;
}

bool UpdateMuditaOS::unpackFileToTemp(mtar_header_t &h, unsigned long *crc32)
{
    auto readBuf            = std::make_unique<unsigned char[]>(boot::consts::tar_buf);
    const fs::path fullPath = getUpdateTmpChild(h.name);

    uint32_t blocksToRead = (h.size / boot::consts::tar_buf) + 1;
    uint32_t sizeToRead   = boot::consts::tar_buf;
    fileExtracted         = h.name;
    fileExtractedSize     = h.size;

    informUpdate(status, "Unpack %s", fullPath.filename().c_str());

    if (crc32 != nullptr) {
        *crc32 = 0;
    }
    else {
        return false;
    }

    int errCode = MTAR_ESUCCESS;
    auto *fp    = std::fopen(fullPath.c_str(), "w+");
    if (fp == nullptr) {
        informError(
            updateos::UpdateError::CantWriteToFile, "unpackFileToTemp %s can't open for writing", fullPath.c_str());
        return false;
    }

    CRC32 digest;

    for (uint32_t i = 0; i < blocksToRead; i++) {
        if (i + 1 == blocksToRead) {
            sizeToRead = h.size % boot::consts::tar_buf;
        }
        else {
            sizeToRead = boot::consts::tar_buf;
        }

        if (sizeToRead == 0)
            break;

        if ((errCode = mtar_read_data(&updateTar, readBuf.get(), sizeToRead)) != MTAR_ESUCCESS) {
            informError(
                updateos::UpdateError::CantWriteToFile, "unpackFileToTemp mtar_read_data failed, errCode=%d", errCode);
            return false;
        }

        const uint32_t dataWritten = std::fwrite(readBuf.get(), 1, sizeToRead, fp);
        if (dataWritten != sizeToRead) {
            informError(
                updateos::UpdateError::CantWriteToFile, "unpackFileToTemp %s can't write to file", fullPath.c_str());
            std::fclose(fp);
            return false;
        }

        currentExtractedBytes += dataWritten;

        digest.add(readBuf.get(), sizeToRead);
    }
    std::fclose(fp);

    *crc32 = digest.getHashValue();
    return true;
}

updateos::UpdateError UpdateMuditaOS::cleanupAfterUpdate()
{
    try {
        if (std::filesystem::is_directory(updateTempDirectory.c_str())) {
            const auto numOfFilesRemoved = std::filesystem::remove_all(updateTempDirectory.c_str());
            LOG_DEBUG("Number of files and directories removed: %lu", static_cast<unsigned long>(numOfFilesRemoved));
        }
    }
    catch (const std::filesystem::filesystem_error &e) {
        LOG_ERROR("remove_all on %s, error %s", updateTempDirectory.c_str(), e.what());
    }

    try {
        mtar_close(&updateTar);
        if (!std::filesystem::remove(updateFile)) {
            return informError(updateos::UpdateError::CantRemoveUpdateFile, "Failed to delete %s", updateFile.c_str());
        }
        LOG_DEBUG("Deleted update file %s", updateFile.c_str());
    }
    catch (const std::filesystem::filesystem_error &fsError) {
        LOG_ERROR("remove on %s, error %s", updateFile.c_str(), fsError.what());
    }

    status = updateos::UpdateState::ReadyForReset;

    informUpdateWindow();

    return updateos::UpdateError::NoError;
}

const fs::path UpdateMuditaOS::getUpdateTmpChild(const fs::path &childPath)
{
    if (childPath.string().rfind("./", 0) == 0) {
        return updateTempDirectory / childPath.string().substr(2);
    }
    else {
        return updateTempDirectory / childPath;
    }
}

updateos::UpdateError UpdateMuditaOS::prepareTempDirForUpdate(const std::filesystem::path &temporaryPath,
                                                              const std::filesystem::path &updatesOSPath)
{
    status = updateos::UpdateState::CreatingDirectories;

    if (isUpdateToBeAborted()) {
        setUpdateAbortFlag(false);
        sys::SystemManager::Reboot(owner);
        return updateos::UpdateError::UpdateAborted;
    }
    else {
        informUpdateWindow();
    }

    updateTempDirectory = temporaryPath / utils::filesystem::generateRandomId(updateos::prefix_len);

    informDebug("Temp dir for update %s", updateTempDirectory.c_str());

    if (!std::filesystem::is_directory(updatesOSPath)) {
        if (!std::filesystem::create_directory(updatesOSPath)) {
            return informError(
                updateos::UpdateError::CantCreateUpdatesDir, "%s can't create it", updatesOSPath.c_str());
        }
    }

    if (!std::filesystem::is_directory(updatesOSPath)) {
        if (!std::filesystem::create_directory(updatesOSPath)) {
            return informError(
                updateos::UpdateError::CantCreateUpdatesDir, "%s can't create it %s", updatesOSPath.c_str());
        }
        else {
            informDebug("prepareTempDirForUpdate %s created", updatesOSPath.c_str());
        }
    }
    else {
        informDebug("prepareTempDirForUpdate %s exists", updatesOSPath.c_str());
    }

    if (!std::filesystem::is_directory(temporaryPath)) {
        informDebug("prepareTempDirForUpdate %s is not a directory", temporaryPath.c_str());
        if (!std::filesystem::create_directory(temporaryPath.c_str())) {
            return informError(updateos::UpdateError::CantCreateTempDir, "can't create %s", temporaryPath.c_str());
        }
        else {
            informDebug("prepareTempDirForUpdate %s created", temporaryPath.c_str());
        }
    }
    else {
        informDebug("prepareTempDirForUpdate %s exists", temporaryPath.c_str());
    }

    if (std::filesystem::is_directory(updateTempDirectory.c_str())) {
        informDebug("prepareTempDirForUpdate %s exists already, try to remove it", updateTempDirectory.c_str());
        try {
            std::filesystem::remove_all(updateTempDirectory.c_str());
        }
        catch (const std::filesystem::filesystem_error &e) {
            return informError(updateos::UpdateError::CantRemoveUniqueTmpDir,
                               "prepareTempDirForUpdate can't remove %s",
                               updateTempDirectory.c_str());
        }
        informDebug("prepareTempDirForUpdate %s removed", updateTempDirectory.c_str());
    }

    informDebug("prepareTempDirForUpdate trying to create %s as tempDir", updateTempDirectory.c_str());
    if (!std::filesystem::create_directory(updateTempDirectory.c_str())) {
        informError(updateos::UpdateError::CantCreateUniqueTmpDir,
                    "prepareTempDirForUpdate failed to create: %s",
                    updateTempDirectory.c_str());
    }

    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::writeBootloader(fs::path bootloaderFile)
{
    status = updateos::UpdateState::UpdatingBootloader;

#if defined(TARGET_Linux)
    return updateos::UpdateError::NoError;
#else

    if (std::filesystem::exists(bootloaderFile.c_str()) == false) {
        return informError(updateos::UpdateError::NoBootloaderFile,
                           "[Bootloader Update] File %s doesn't exist!\n",
                           bootloaderFile.c_str());
    }

    auto fileHandler = std::fopen(bootloaderFile.c_str(), "r");
    if (fileHandler == nullptr) {
        std::fclose(fileHandler);
        return informError(updateos::UpdateError::CantOpenBootloaderFile,
                           "[Bootloader Update] Failed to open file %s\n",
                           bootloaderFile.c_str());
    }

    unsigned long fileLen = std::filesystem::file_size(bootloaderFile);
    auto fileBuf          = std::make_unique<uint8_t[]>(fileLen);
    if (fileBuf == nullptr) {
        std::fclose(fileHandler);
        return informError(updateos::UpdateError::CantAllocateBuffer,
                           "[Bootloader Update] Failed to allocate buffer\n");
    }

    auto filesLoaded = std::fread(fileBuf.get(), fileLen, 1, fileHandler);
    if (filesLoaded == 0) {
        std::fclose(fileHandler);
        return informError(updateos::UpdateError::CantOpenBootloaderFile,
                           "[Bootloader Update] Failed to load file %s\n",
                           bootloaderFile.c_str());
    }
    std::fclose(fileHandler);

    informUpdate(status, "[Bootloader Update] File size: %lu B, Writing...", fileLen);

    bsp::eMMC emmc;
    emmc.Init();
    emmc.SwitchPartition(bsp::eMMC::Partition::Boot1);
    emmc.WriteBlocks(fileBuf.get(), 0, std::ceil(fileLen / FSL_SDMMC_DEFAULT_BLOCK_SIZE));

    informUpdate(status, "[Bootloader Update] DONE!\n");
    emmc.SwitchPartition(bsp::eMMC::Partition::UserArea);
    return updateos::UpdateError::NoError;
#endif
}

const json11::Json UpdateMuditaOS::getVersionInfoFromFile(const fs::path &updateFile)
{
    if (std::filesystem::exists(updateFile.c_str())) {
        mtar_t tar;
        mtar_header_t tarHeader;

        if (mtar_open(&tar, updateFile.c_str(), "r") == MTAR_EOPENFAIL) {
            LOG_WARN("%s can't open", updateFile.c_str());
            return json11::Json();
        }

        std::unique_ptr<char[]> versionFilename = std::make_unique<char[]>(boot::consts::crc_buf);
        sprintf(versionFilename.get(), "./%s", updateos::file::version);
        if (mtar_find(&tar, versionFilename.get(), &tarHeader) == MTAR_ENOTFOUND) {
            LOG_WARN("can't find %s in %s", updateos::file::version, updateFile.c_str());

            mtar_close(&tar);
            return json11::Json();
        }

        /* this file should never be larger then boot::consts::tar_buf */
        std::unique_ptr<char[]> readBuf = std::make_unique<char[]>(boot::consts::tar_buf);
        if (mtar_read_data(&tar, readBuf.get(), tarHeader.size) != MTAR_ESUCCESS) {
            LOG_WARN("can't read %s in %s", updateos::file::version, updateFile.c_str());

            return json11::Json();
        }

        mtar_close(&tar);

        std::string parserError;
        std::string dataPackage  = std::string(static_cast<char *>(readBuf.get()), tarHeader.size);
        json11::Json versionInfo = json11::Json::parse(dataPackage, parserError);
        if (parserError != "") {
            LOG_WARN("can't parse %s as JSON error: \"%s\"", updateos::file::version, parserError.c_str());
            return json11::Json();
        }

        return versionInfo;
    }
    else {
        LOG_WARN("%s does not exist", updateFile.c_str());
    }

    return json11::Json();
}

bool UpdateMuditaOS::isUpgradeToCurrent(const std::string &versionToCompare)
{
    return true;
}

const fs::path UpdateMuditaOS::checkForUpdate()
{
    const auto updatesOSPath = purefs::dir::getUpdatesOSPath();
    std::error_code errorCode;
    if (!std::filesystem::is_directory(updatesOSPath, errorCode)) {
        LOG_INFO("%s does not exist, try to create", updatesOSPath.c_str());
        std::filesystem::create_directory(updatesOSPath, errorCode);
        if (errorCode) {
            LOG_WARN("%s can't be created \"%s\"", updatesOSPath.c_str(), errorCode.message().c_str());
            return fs::path();
        }
    }

    for (auto &file : std::filesystem::directory_iterator(updatesOSPath.c_str(), errorCode)) {
        json11::Json versionInfo = UpdateMuditaOS::getVersionInfoFromFile(updatesOSPath / file.path());
        if (versionInfo.is_null())
            continue;
        if (errorCode) {
            LOG_WARN("directory_iterator for %s failed \"%s\"", updatesOSPath.c_str(), errorCode.message().c_str());
            return fs::path();
        }

        if (versionInfo[boot::json::os_version][boot::json::version_string].is_string()) {
            if (UpdateMuditaOS::isUpgradeToCurrent(
                    versionInfo[boot::json::os_version][boot::json::version_string].string_value())) {
                return updatesOSPath / file.path();
            }
        }
    }

    return fs::path();
}

updateos::UpdateError UpdateMuditaOS::updateUserData()
{
    return updateos::UpdateError::NoError;
}

updateos::UpdateError UpdateMuditaOS::informError(const updateos::UpdateError errorCode, const char *format, ...)
{
    va_list argptr;
    std::unique_ptr<char[]> readBuf(new char[boot::consts::tar_buf]);
    va_start(argptr, format);
    vsnprintf(readBuf.get(), boot::consts::tar_buf, format, argptr);
    va_end(argptr);

    LOG_ERROR("UPDATE_ERRROR [%d] %s", static_cast<uint8_t>(errorCode), readBuf.get());

    auto msgToSend         = std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateError);
    messageText            = std::string(readBuf.get());
    msgToSend->updateStats = (updateos::UpdateStats)(*this);
    owner->bus.sendUnicast(msgToSend, app::name_desktop);

    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::InternalServerError);
    responseContext.setEndpoint(parserFSM::EndpointType::update);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::update, parserFSM::json::updateError},
                                                     {parserFSM::json::status, messageText},
                                                     {parserFSM::json::errorCode, static_cast<uint8_t>(errorCode)}};
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());

    updateRunStatus.finishedState = status;
    updateRunStatus.finishedError = errorCode;
    return errorCode;
}

void UpdateMuditaOS::informDebug(const char *format, ...)
{
    va_list argptr;
    std::unique_ptr<char[]> readBuf(new char[boot::consts::tar_buf]);
    va_start(argptr, format);
    vsnprintf(readBuf.get(), boot::consts::tar_buf, format, argptr);
    va_end(argptr);

    LOG_DEBUG("UPDATE_DEBUG %s", readBuf.get());
}

void UpdateMuditaOS::informUpdate(const updateos::UpdateState statusCode, const char *format, ...)
{
    va_list argptr;
    std::unique_ptr<char[]> readBuf(new char[boot::consts::tar_buf]);
    va_start(argptr, format);
    vsnprintf(readBuf.get(), boot::consts::tar_buf, format, argptr);
    va_end(argptr);

    LOG_INFO("UPDATE_INFO [%d] %s", static_cast<uint8_t>(statusCode), readBuf.get());

    auto msgToSend         = std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateInform);
    messageText            = std::string(readBuf.get());
    msgToSend->updateStats = (updateos::UpdateStats)(*this);
    if (owner == nullptr) {
        return;
    }
    owner->bus.sendUnicast(msgToSend, app::name_desktop);

    parserFSM::Context responseContext;
    responseContext.setResponseStatus(parserFSM::http::Code::Accepted);
    responseContext.setEndpoint(parserFSM::EndpointType::update);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::update, parserFSM::json::updateInfo},
                                                     {parserFSM::json::status, messageText},
                                                     {parserFSM::json::statusCode, static_cast<uint8_t>(statusCode)}};
    responseContext.setResponseBody(responseJson);
    parserFSM::MessageHandler::putToSendQueue(responseContext.createSimpleResponse());

    updateRunStatus.finishedState = status;
}

void UpdateMuditaOS::setInitialHistory(const std::string &initialHistory)
{
    LOG_DEBUG("%s", initialHistory.c_str());
    std::string parseErrors;
    updateHistory = json11::Json::parse(initialHistory, parseErrors);

    if (!parseErrors.empty() && !initialHistory.empty()) {
        LOG_WARN("Can't parse current update history, resetting");
        updateHistory = json11::Json();
    }
}

void UpdateMuditaOS::informUpdateWindow()
{
    fs::path file  = UpdateMuditaOS::checkForUpdate();
    auto msgToSend = std::make_shared<sdesktop::UpdateOsMessage>(updateos::UpdateMessageType::UpdateNow, file);
    msgToSend->updateStats.versionInformation = UpdateMuditaOS::getVersionInfoFromFile(file);
    msgToSend->updateStats.status             = status;
    if (owner != nullptr) {
        owner->bus.sendUnicast(msgToSend, app::name_desktop);
    }
}

void UpdateMuditaOS::storeRunStatusInDB()
{
    std::vector<json11::Json> tempTable;
    bool statusRunFound = false;
    if (updateHistory.is_array()) {
        for (const auto &value : updateHistory.array_items()) {
            try {
                // need to use stoul as json does not seem to handle it well
                if (std::stoul(value[updateos::settings::startTime].string_value()) == updateRunStatus.startTime) {
                    tempTable.emplace_back(updateRunStatus);

                    // this tells us we already found and element in history
                    statusRunFound = true;
                }
                else {
                    // if we found a value in history that's not ours, just copy it to temptable
                    tempTable.emplace_back(value);
                }
            }
            catch (const std::exception &arg) {
                LOG_ERROR("storeRunStatusInDB %s error - %s",
                          arg.what(),
                          value[updateos::settings::startTime].string_value().c_str());
            }
        }

        if (!statusRunFound) {
            // if our element was not found, insert it
            tempTable.emplace_back(updateRunStatus);
        }
    }
    else {
        // if the history is not a json array, initialize it
        tempTable = json11::Json::array{updateRunStatus};
    }

    updateHistory = tempTable;
    owner->storeHistory(updateHistory.dump());
}

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

#pragma once

#include <json11.hpp>
#include <microtar.hpp>
#include <boot/bootconfig.hpp>
#include <purefs/filesystem_paths.hpp>
#include <endpoints/update/UpdateOSTypes.hpp>

#include <cstdint>
#include <filesystem>
#include <iosfwd>
#include <string>
#include <vector>
#include <atomic>

class ServiceDesktop;

struct FileInfo
{
    FileInfo(mtar_header_t &h, unsigned long crc32);
    json11::Json to_json() const;

    std::string fileName;
    std::size_t fileSize;
    unsigned long fileCRC32;
};

class UpdateMuditaOS : public updateos::UpdateStats
{
  public:
    explicit UpdateMuditaOS(ServiceDesktop *ownerService);

    updateos::UpdateError runUpdate();
    updateos::UpdateError prepareTempDirForUpdate(const std::filesystem::path &temporaryPath,
                                                  const std::filesystem::path &updatesOSPath);
    updateos::UpdateError unpackUpdate();
    updateos::UpdateError verifyChecksums();
    updateos::UpdateError verifyVersion();
    updateos::UpdateError updateBootloader();
    updateos::UpdateError prepareRoot();
    updateos::UpdateError updateBootJSON();
    updateos::UpdateError setUpdateFile(const std::filesystem::path &updatesOSPath, const fs::path &updateFileToUse);
    updateos::UpdateError cleanupAfterUpdate();
    updateos::UpdateError updateUserData();

    updateos::UpdateError informError(updateos::UpdateError errorCode, const char *format, ...);
    void informDebug(const char *format, ...);
    void informUpdate(updateos::UpdateState statusCode, const char *format, ...);

    updateos::UpdateError writeBootloader(fs::path bootloaderFile);

    void getChecksumInfo(const std::string &infoLine, std::string &filePath, unsigned long *fileCRC32Long);
    unsigned long getExtractedFileCRC32(const std::string &filePath);
    bool unpackFileToTemp(mtar_header_t &header, unsigned long *crc32);
    const fs::path getUpdateTmpChild(const fs::path &childPath);

    static const json11::Json getVersionInfoFromFile(const fs::path &updateFile);
    static bool isUpgradeToCurrent(const std::string &versionToCompare);
    static const fs::path checkForUpdate();
    void historyValueChanged(const std::string &value);
    void setInitialHistory(const std::string &initialHistory);
    void informUpdateWindow();
    json11::Json getUpdateHistory() const
    {
        return updateHistory;
    }
    void setUpdateAbortFlag(bool flag)
    {
        updateAbort = flag;
    }
    bool isUpdateToBeAborted() const noexcept
    {
        return updateAbort;
    }

  private:
    std::vector<FileInfo> filesInUpdatePackage;
    mtar_t updateTar      = {};
    std::atomic_bool updateAbort = false;
    ServiceDesktop *owner = nullptr;

    void storeRunStatusInDB();

    updateos::UpdateRunStatus updateRunStatus;
    json11::Json updateHistory;
    json11::Json targetVersionInfo;
    boot::BootConfig bootConfig;
};

D module-services/service-desktop/endpoints/update/UpdateOSTypes.hpp => module-services/service-desktop/endpoints/update/UpdateOSTypes.hpp +0 -121
@@ 1,121 0,0 @@

// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <purefs/filesystem_paths.hpp>

namespace fs = std::filesystem;
namespace updateos
{
    inline constexpr auto initSysVer = "0.00.0";

    namespace file
    {
        inline constexpr auto checksums = "checksums.txt";
        inline constexpr auto sql_mig   = "sqlmig.json";
        inline constexpr auto version   = "version.json";
    } // namespace file

    namespace settings
    {
        inline constexpr auto history       = "history";
        inline constexpr auto startTime     = "startTime";
        inline constexpr auto endTime       = "endTime";
        inline constexpr auto finishedState = "finishedState";
        inline constexpr auto finishedError = "finishedError";
        inline constexpr auto fromVersion   = "fromVersion";
        inline constexpr auto toVersion     = "toVersion";
    } // namespace settings
    namespace extension
    {
        inline constexpr auto update = ".tar";
    }

    inline constexpr auto prefix_len = 8;

    enum class UpdateError
    {
        NoError,
        CantCreateTempDir,
        CantCreateUpdatesDir,
        CantRemoveUniqueTmpDir,
        CantRemoveUpdateFile,
        CantCreateUniqueTmpDir,
        CantCreateExtractedFile,
        CantOpenChecksumsFile,
        VerifyChecksumsFailure,
        VerifyVersionFailure,
        CantWriteBootloader,
        CantOpenUpdateFile,
        CantDeletePreviousOS,
        CantRenameCurrentToPrevious,
        CantRenameTempToCurrent,
        CantUpdateJSON,
        CantSaveJSON,
        CantUpdateCRC32JSON,
        CantDeltreePreviousOS,
        CantWriteToFile,
        NoBootloaderFile,
        CantOpenBootloaderFile,
        CantAllocateBuffer,
        CantLoadBootloaderFile,
        UpdateAborted,
        CantCopyTempToCurrent
    };

    enum class UpdateState
    {
        Initial,
        UpdateFileSet,
        CreatingDirectories,
        ExtractingFiles,
        UpdatingBootloader,
        ChecksumVerification,
        VersionVerificiation,
        ReadyForReset
    };

    enum class UpdateMessageType
    {
        UpdateFoundOnBoot,
        UpdateCheckForUpdateOnce,
        UpdateNow,
        UpdateError,
        UpdateInform
    };

    struct UpdateStats
    {
        fs::path updateFile            = "";
        fs::path fileExtracted         = "";
        fs::path updateTempDirectory   = purefs::dir::getTemporaryPath();
        uint32_t totalBytes            = 0;
        uint32_t currentExtractedBytes = 0;
        uint32_t fileExtractedSize     = 0;
        uint32_t uuid                  = 0;
        std::string messageText;
        updateos::UpdateState status;
        json11::Json versionInformation;
    };

    struct UpdateRunStatus
    {
        uint32_t startTime = 0, endTime = 0;
        UpdateState finishedState = UpdateState::Initial;
        UpdateError finishedError = UpdateError::NoError;
        json11::Json fromVersion, toVersion;

        json11::Json to_json() const
        {
            return json11::Json::object{{updateos::settings::startTime, std::to_string(startTime)},
                                        {updateos::settings::endTime, std::to_string(endTime)},
                                        {updateos::settings::finishedState, static_cast<int>(finishedState)},
                                        {updateos::settings::finishedError, static_cast<int>(finishedError)},
                                        {updateos::settings::fromVersion, fromVersion},
                                        {updateos::settings::toVersion, toVersion}};
        }
    };

}; // namespace updateos

M module-services/service-desktop/service-desktop/DesktopMessages.hpp => module-services/service-desktop/service-desktop/DesktopMessages.hpp +0 -49
@@ 4,7 4,6 @@
#pragma once

#include <endpoints/developerMode/DeveloperModeEndpoint.hpp>
#include <endpoints/update/UpdateOSTypes.hpp>
#include <service-appmgr/Actions.hpp>
#include <service-appmgr/messages/ActionRequest.hpp>
#include <Service/Message.hpp>


@@ 14,35 13,6 @@

namespace sdesktop
{
    class UpdateOsMessage : public sys::DataMessage
    {
      public:
        UpdateOsMessage(const std::string updateFilePath, const uint32_t requestUUID)
            : sys::DataMessage(MessageType::UpdateOS)
        {
            updateStats.updateFile = updateFilePath;
            updateStats.uuid       = requestUUID;
        };

        UpdateOsMessage() : sys::DataMessage(MessageType::UpdateOS)
        {}

        UpdateOsMessage(const updateos::UpdateMessageType updateMessageType)
            : sys::DataMessage(MessageType::UpdateOS), messageType(updateMessageType)
        {}

        UpdateOsMessage(const updateos::UpdateMessageType updateMessageType, fs::path updateFileFoundOnBoot)
            : sys::DataMessage(MessageType::UpdateOS), messageType(updateMessageType)
        {
            updateStats.updateFile = updateFileFoundOnBoot;
        }

        ~UpdateOsMessage() override = default;

        updateos::UpdateStats updateStats       = {};
        updateos::UpdateMessageType messageType = updateos::UpdateMessageType::UpdateNow;
    };

    class BackupMessage : public sys::DataMessage
    {
      public:


@@ 138,23 108,4 @@ namespace sdesktop
            explicit ScreenlockCheckEvent(bool isLocked);
        };
    } // namespace developerMode

    namespace transfer
    {
        class TransferTimerState : public sys::DataMessage
        {
          public:
            enum Request
            {
                None,
                Start,
                Reload,
                Stop
            };
            enum Request req = Request::None;
            TransferTimerState(enum Request req = None) : sys::DataMessage(MessageType::TransferTimer), req(req){};
            ~TransferTimerState() override = default;
        };
    } // namespace transfer

} // namespace sdesktop

M module-services/service-desktop/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/service-desktop/ServiceDesktop.hpp +0 -3
@@ 94,10 94,8 @@ class ServiceDesktop : public sys::Service
    sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override;
    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp) override;

    std::unique_ptr<UpdateMuditaOS> updateOS;
    std::unique_ptr<WorkerDesktop> desktopWorker;

    void storeHistory(const std::string &historyValue);
    void prepareBackupData();
    void prepareRestoreData(const std::filesystem::path &restoreLocation);
    const BackupRestoreStatus getBackupRestoreStatus()


@@ 116,7 114,6 @@ class ServiceDesktop : public sys::Service
  private:
    std::unique_ptr<sdesktop::USBSecurityModel> usbSecurityModel;
    std::unique_ptr<settings::Settings> settings;
    sys::TimerHandle transferTimer;
    std::unique_ptr<sdesktop::bluetooth::BluetoothMessagesHandler> btMsgHandler;

    static constexpr unsigned int DefaultLogFlushTimeoutInMs = 1000U;

M module-services/service-desktop/service-desktop/WorkerDesktop.hpp => module-services/service-desktop/service-desktop/WorkerDesktop.hpp +1 -35
@@ 22,19 22,9 @@ namespace constants
    constexpr auto usbSuspendTimeout = std::chrono::seconds{1};
} // namespace constants

class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
class WorkerDesktop : public sys::Worker
{
  public:
    enum TransferFailAction
    {
        doNothing,
        removeDesitnationFile
    };
    enum class Command
    {
        CancelTransfer,
    };

    WorkerDesktop(sys::Service *ownerServicePtr,
                  const sdesktop::USBSecurityModel &securityModel,
                  const std::string serialNumber);


@@ 50,36 40,13 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
        return receiveQueue;
    }

    sys::ReturnCodes startDownload(const std::filesystem::path &destinationPath,
                                   const uint32_t fileSize,
                                   std::string fileCrc32);
    void stopTransfer(const TransferFailAction action);

    void cancelTransferOnTimeout();

    void rawDataReceived(void *dataPtr, uint32_t dataLen) override;
    bool getRawMode() const noexcept override;

  private:
    void uploadFileFailedResponse();

    void transferTimeoutHandler();

    void startTransferTimer();
    void stopTransferTimer();
    void reloadTransferTimer();
    void suspendUsb();

    bool stateChangeWait();

    xQueueHandle receiveQueue;
    xQueueHandle irqQueue;
    FILE *fileDes                  = nullptr;
    uint32_t writeFileSizeExpected = 0;
    uint32_t writeFileDataWritten  = 0;
    std::string expectedFileCrc32;
    std::filesystem::path filePath;
    std::atomic<bool> rawModeEnabled = false;
    const sdesktop::USBSecurityModel &securityModel;
    const std::string serialNumber;
    sys::Service *ownerService = nullptr;


@@ 88,5 55,4 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
    bsp::USBDeviceStatus usbStatus = bsp::USBDeviceStatus::Disconnected;

    std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    CRC32 digestCrc32;
};

M module-services/service-desktop/tests/test-fs-ep-operations.cpp => module-services/service-desktop/tests/test-fs-ep-operations.cpp +2 -2
@@ 45,7 45,7 @@ TEST_CASE("Endpoint Filesystem Test")
        auto fileToSend  = "\"/sys/user/data/applications/settings/quotes.json\"";
        auto fileSize    = 676u;
        auto fileCrc32   = "\"37ef9a52\"";
        auto chunkSize   = 12288u;
        auto chunkSize   = FileOperations::ChunkSize;
        auto txID        = 1u;
        auto testMessage = "{\"endpoint\":" + std::to_string(endpoint) +
                           ", \"method\": 3, \"uuid\":" + std::to_string(uuid) +


@@ 130,7 130,7 @@ TEST_CASE("Endpoint Filesystem Test")
        auto uuid        = 1103;
        auto fileToGet   = "\"/sys/user/data/applications/settings/quotes.json\"";
        auto fileSize    = 676u;
        auto chunkSize   = 12288u;
        auto chunkSize   = FileOperations::ChunkSize;
        auto rxID        = 2u;
        auto testMessage = "{\"endpoint\":" + std::to_string(endpoint) +
                           ", \"method\":1, \"uuid\":" + std::to_string(uuid) +

M module-services/service-evtmgr/EventManager.cpp => module-services/service-evtmgr/EventManager.cpp +1 -0
@@ 46,6 46,7 @@
#include <SystemManager/messages/PhoneModeRequest.hpp>
#include <vibra/Vibra.hpp>
#include <ticks.hpp>
#include <purefs/filesystem_paths.hpp>

namespace
{

M module-sys/SystemManager/SystemManager.cpp => module-sys/SystemManager/SystemManager.cpp +2 -33
@@ 227,21 227,6 @@ namespace sys
        return true;
    }

    bool SystemManager::Update(Service *s, const std::string &updateOSVer, const std::string &currentOSVer)
    {
        // set update OS version (and also current os version) in Settings
        storeOsVersion(s, updateOSVer, currentOSVer);

        // close some services
        s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::Update), service::name::system_manager);

        // close some applications
        auto msgCloseApplications = std::make_shared<app::manager::UpdateInProgress>(service::name::system_manager);
        s->bus.sendUnicast(std::move(msgCloseApplications), app::manager::ApplicationManager::ServiceName);

        return true;
    }

    bool SystemManager::Restore(Service *s)
    {
        LOG_DEBUG("trying to enter restore state");


@@ 251,24 236,9 @@ namespace sys
        if (ret.first != ReturnCodes::Success) {
            LOG_WARN("Can't stop all services, %d ms wait time", sys::constants::restoreTimeout);
        }
        auto msgCloseApplications = std::make_shared<app::manager::UpdateInProgress>(service::name::system_manager);
        ret                       = s->bus.sendUnicastSync(std::move(msgCloseApplications),
                                     app::manager::ApplicationManager::ServiceName,
                                     sys::constants::restoreTimeout);
        if (ret.first != ReturnCodes::Success) {
            LOG_WARN("Can't stop all applications, %d ms wait time", sys::constants::restoreTimeout);
        }
        return true;
    }

    void SystemManager::storeOsVersion(Service *s, const std::string &updateOSVer, const std::string &currentOSVer)
    {
        // store OS version in Settings
        auto msgSetUpdateVersion = std::make_shared<app::manager::SetOsUpdateVersion>(
            service::name::system_manager, updateOSVer, currentOSVer);
        s->bus.sendUnicast(std::move(msgSetUpdateVersion), app::manager::ApplicationManager::ServiceName);
    }

    bool SystemManager::Reboot(Service *s)
    {
        s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::Reboot), service::name::system_manager);


@@ 277,9 247,8 @@ namespace sys

    bool SystemManager::RebootToUpdate(Service *s, UpdateReason updateReason)
    {
        s->bus.sendUnicast(
            std::make_shared<SystemManagerCmd>(Code::RebootToUpdate, CloseReason::RegularPowerDown, updateReason),
            service::name::system_manager);
        s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::RebootToUpdate, CloseReason::Reboot, updateReason),
                           service::name::system_manager);
        return true;
    }


M module-sys/SystemManager/SystemManager.hpp => module-sys/SystemManager/SystemManager.hpp +0 -4
@@ 105,16 105,12 @@ namespace sys
        // Invoke system close procedure
        static bool CloseSystem(Service *s);

        static bool Update(Service *s, const std::string &updateOSVer, const std::string &currentOSVer);

        static bool Restore(Service *s);

        static bool Reboot(Service *s);

        static bool RebootToUpdate(Service *s, UpdateReason updateReason);

        static void storeOsVersion(Service *s, const std::string &updateOSVer, const std::string &currentOSVer);

        static bool SuspendService(const std::string &name, Service *caller);

        static bool ResumeService(const std::string &name, Service *caller);

D test/firmware_update_test/.gdbinit-1051-autocontinue => test/firmware_update_test/.gdbinit-1051-autocontinue +0 -10
@@ 1,10 0,0 @@
set pagination off
target remote localhost:2331
monitor reset 0
monitor halt
monitor memU32 0x401BC000 = 128;
load 
eval "monitor exec SetRTTAddr %p", &_SEGGER_RTT
info threads
thread 2
continue

D test/firmware_update_test/README.md => test/firmware_update_test/README.md +0 -39
@@ 1,39 0,0 @@
## tl;dr

    - `pip install GitPython tqdm`
    - `sudo apt install rhash`
    -  do actions from `doc/download_assets.md`
    - `make package-update`
    - plug your phone to the USB port and power on (if turned off)
    - `python3 ./firmware_update_test/update.py tarball_path`
    - if your phone doesn't restart during the update - **restart it** manually

# Firmware update test


The aim of this test is to check the correctness of the `service-desktop` API and upgrade the phone software.

This test is based on [Harness](../README.md) class and uses `pyTest` to verify the update process. It makes use of `service-desktop` 
API to upload a tarball update package to the phone's memory and execute the update. After a successful update the phone should reset
and an update check will be performed. In the end, an update verification status will be shown in the console (along with the exit code).

## Usage
To be able to conduct the test (or just use the script as an update utility) a few prerequisites have to be met:

* Pure needs connected via USB
* Your Python version has to be at least 3.8
* Your Python virtual environment needs to be properly set up (as mentioned in PyTest's [readme](../README.md))

After all of the requirements are fulfilled, the script can be safely executed:

```python
python3 ./update.py tarball_path
```
The serial port used by the phone will be detected automatically.


To obtain the tarball package use the proper CMake target: `make package-update`. The only requirement is having a properly configured API token and Python environment, required by the asset downloading script.
Extended docs are here: `doc/download_assets.md`

Missing info: required python packages -  `GitPython` and `tqdm`. Additionally: `rhash`


D test/firmware_update_test/update.py => test/firmware_update_test/update.py +0 -162
@@ 1,162 0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import sys
import time
import sys
import os.path
import atexit
import json

from tqdm import tqdm

sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

from harness import log
from harness.harness import Harness
from harness.interface.defs import key_codes, endpoint, method
from harness.utils import Timeout
from harness.interface.error import TestError, Error
from harness.api.developermode import PhoneModeLock
from functools import partial

def set_passcode(harness: Harness, flag: bool):
    '''
    on exit -> restore PhoneModeLock
    '''
    PhoneModeLock(flag).run(harness)

# uploaded file chunk size - according to
# https://appnroll.atlassian.net/wiki/spaces/MFP/pages/656637953/Protocol+description
CHUNK_SIZE = 1024 * 128

# update performing timeout
UPDATE_TIMEOUT = 300

update_status_code = {
    0: "Initial",
    1: "UpdateFileSet",
    2: "CreatingDirectories",
    3: "ExtractingFiles",
    4: "UpdatingBootloader",
    5: "ChecksumVerification",
    6: "VersionVerification",
    7: "ReadyForReset"
}


def update(harness, update_filepath: str):
    connection = harness.get_connection()
    serial = harness.get_connection().get_serial()
    file_size = os.path.getsize(update_filepath)
    filename = update_filepath.split('/')[-1]
    body = {"command": "download", "fileName": filename, "fileSize": file_size}

    ret = harness.endpoint_request("filesystem", "post", body)["body"]
    if ret["status"] is not None:
        log.info(f"Update status: {update_status_code[int(ret['status'])]}")

    log.info("Downloading update file to the target")
    with open(update_filepath, 'rb') as file:
        with tqdm(total=file_size, unit='B', unit_scale=True) as p_bar:
            for chunk in iter(partial(file.read, CHUNK_SIZE), b''):
                p_bar.update(CHUNK_SIZE)
                serial.write(chunk)
    print(" ")

    body = {"fileName": filename}
    ret = harness.endpoint_request("update", "post", body)["body"]
    if ret["fileName"] != filename and int(ret[fileSize]) != file_size:
        log.error("Upload error!")
        exit(1)
    timer = time.time()

    while True:
        if time.time() - timer > UPDATE_TIMEOUT:
            log.error("Update timeout!")
            return False
        if serial.in_waiting > 0:
            result = connection.read(10)[0]
            ret = json.loads(result)
            body = ret['body']
            if "status" in body:
                status = body["status"]
                log.info(f"Update status: {status}")
                timer = time.time()
                if "reset" in status:
                    log.info("Update finished, wait for device reset...")
                    return True


def get_update_list(harness):
    harness.unlock_phone()
    ret = harness.endpoint_request("deviceInfo", "get", {})
    device_info = ret["body"]
    update_history = device_info["updateHistory"]
    failed_updates = 0

    if update_history is None:
        log.info("Update history clean!")
        return [None, 0]

    for update in update_history:
        if update["finishedError"] != 0 and update["finishedState"] != 6:
            failed_updates = failed_updates + 1

    log.info(f"Found {len(update_history)} update entries with {failed_updates} failed updates")
    return [update_history, failed_updates]


def main():
    if len(sys.argv) == 1:
        print(f'Please pass update file path as the parameter: python {sys.argv[0]} file_path ')
        raise TestError(Error.PORT_NOT_FOUND)

    harness = None

    with Timeout.limit(seconds=20):
        while not harness:
            try:
                harness = Harness.from_detect()
            except TestError:
                pass

    atexit.register(set_passcode, harness, True)
    set_passcode(harness, False)

    update_filename = str(sys.argv[1])
    history, fails = get_update_list(harness)

    if update(harness, update_filename):
        # wait for reboot
        harness = None
        log.info("Waiting for device to reset")
        time.sleep(5)
        # connect to the phone once again
        with Timeout.limit(seconds=90):
            while not harness:
                try:
                    harness = Harness.from_detect()
                except TestError:
                    pass

        [new_history, new_fails] = get_update_list(harness)
        if new_fails != fails or (history is None) or (history is not None and (len(new_history) != len(history) + 1)):
            log.error("Update failed!")
            exit(1)
        else:
            log.info("Update successful!")
            exit(0)
    else:
        log.error("Update error!")
        exit(1)


if __name__ == "__main__":
    try:
        main()
    except TestError as err:
        log.error(err)
        exit(err.get_error_code())

M test/harness => test/harness +1 -1
@@ 1,1 1,1 @@
Subproject commit eecee32772ba2f7ad838535bc1fc99e707205d8e
Subproject commit 3ea8a6a16d3c2e92968106ce842b54d586e6c7e8

D test/pytest/service-desktop/test_update.py => test/pytest/service-desktop/test_update.py +0 -26
@@ 1,26 0,0 @@
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
from harness.interface.defs import status


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("phone_unlocked")
@pytest.mark.rt1051
def test_update(harness):
    body = {}
    ret = harness.endpoint_request("update", "get", body)
    assert ret["status"] == status["OK"]

    file_list = ret["body"]["updateFileList"]

    if len(file_list) == 0:
        pytest.skip("No update files")

    for file in file_list:
        assert file["size"] != 0

    body = {"filename": file_list[0]["name"]}
    ret = harness.endpoint_request("update", "post", body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["updateReady"] is True

M test/pytest/test_updater.py => test/pytest/test_updater.py +7 -6
@@ 6,11 6,12 @@ import pytest
import logging
from harness import log
from harness.interface.defs import Method, Endpoint
from harness.request import Transaction, Request,TransactionError
from harness.request import Transaction, Request, TransactionError
from harness.rt_harness_discovery import get_rt1051_harness
from harness.harness import Harness
from harness.api.filesystem import put_file, get_file
from harness.api.developermode import PhoneModeLock, PhoneReboot, Reboot
from harness.api.developermode import PhoneModeLock
from harness.api.update import PhoneReboot, Reboot


def get_version(harness: Harness):


@@ 35,13 36,13 @@ def test_update(harness: Harness):

    log.info(get_version(harness))
    PhoneModeLock(False).run(harness)
    put_file(harness, filename, "/sys/user/")
    put_file(harness, filename, "/sys/user")
    PhoneReboot(Reboot.UPDATE).run(harness)
    assert harness.connection.watch_port_reboot(60)
    assert harness.connection.watch_port_reboot(300)

    harness = get_rt1051_harness(60)
    harness = get_rt1051_harness(300)
    import time
    time.sleep(10)
    time.sleep(15)
    harness.unlock_phone()
    PhoneModeLock(False).run(harness)


M third-party/usb_stack => third-party/usb_stack +1 -1
@@ 1,1 1,1 @@
Subproject commit 6e445323a4b0209322a284efd1b3479ed2664df8
Subproject commit df703b04e8073dc52a6f13ce34230c6b2e47bbc4