~aleteoryx/muditaos

3cc558b02add43fa90c548ca220665d84f0c49ca — Hubert Chrzaniuk 5 years ago 3a86d65
[EGD-2931] Fix private number incoming calls

The change adjusts both cellular service as well as
call and call log applications to handle properly
incoming calls from private numbers.
38 files changed, 500 insertions(+), 189 deletions(-)

M image/assets/lang/English.json
M module-apps/application-call/ApplicationCall.cpp
M module-apps/application-call/ApplicationCall.hpp
M module-apps/application-call/data/CallAppStyle.hpp
M module-apps/application-call/windows/CallWindow.cpp
M module-apps/application-calllog/CalllogModel.cpp
M module-apps/application-calllog/data/CallLogStyle.hpp
M module-apps/application-calllog/widgets/CalllogItem.cpp
M module-apps/application-calllog/windows/CallLogDetailsWindow.cpp
M module-apps/application-calllog/windows/CallLogMainWindow.cpp
M module-apps/application-calllog/windows/CallLogMainWindow.hpp
M module-apps/application-calllog/windows/CallLogOptionsWindow.cpp
M module-apps/windows/AppWindow.hpp
M module-cellular/CMakeLists.txt
M module-cellular/at/UrcHandler.hpp
A module-cellular/at/UrcRing.hpp
M module-cellular/at/src/Urc.cpp
M module-cellular/at/src/UrcFactory.cpp
A module-cellular/at/src/UrcRing.cpp
M module-cellular/test/unittest_URC.cpp
M module-db/Interface/CalllogRecord.cpp
M module-services/service-appmgr/model/ApplicationManager.cpp
M module-services/service-appmgr/service-appmgr/Actions.hpp
M module-services/service-cellular/CellularCall.cpp
M module-services/service-cellular/CellularRequestHandler.cpp
M module-services/service-cellular/CellularUrcHandler.cpp
M module-services/service-cellular/CellularUrcHandler.hpp
M module-services/service-cellular/RequestFactory.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-cellular/service-cellular/CellularCall.hpp
M module-services/service-cellular/service-cellular/CellularMessage.hpp
M module-services/service-cellular/service-cellular/CellularRequestHandler.hpp
M module-services/service-cellular/service-cellular/RequestFactory.hpp
M module-services/service-cellular/service-cellular/RequestHandler.hpp
M module-services/service-cellular/service-cellular/ServiceCellular.hpp
A module-services/service-cellular/service-cellular/requests/RejectRequest.hpp
M module-services/service-cellular/tests/unittest_request_factory.cpp
M module-services/service-fota/FotaUrcHandler.hpp
M image/assets/lang/English.json => image/assets/lang/English.json +2 -1
@@ 503,5 503,6 @@
  "app_desktop_update_start": "Update start",
  "app_desktop_update_size": "size",
  "app_desktop_update_bytes": "bytes",
  "app_desktop_update_unpacking": "Unpacking"
  "app_desktop_update_unpacking": "Unpacking",
  "app_call_private_number": "Private number"
}

M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +44 -34
@@ 50,40 50,27 @@ namespace app
            switchWindow(app::window::name_emergencyCall, std::forward<decltype(data)>(data));
            return msgHandled();
        });

        auto convertibleToActionHandler = [this](sys::Message *request) { return HandleMessageAsAction(request); };
        connect(typeid(CellularActionResponseMessage), convertibleToActionHandler);
    }

    auto ApplicationCall::HandleMessageAsAction(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>
    {
        auto actionMsg = dynamic_cast<manager::actions::ConvertibleToAction *>(request);
        if (!actionMsg) {
            return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Failure);
        }

        auto buttonAction = [=]() -> bool {
            returnToPreviousWindow();
            return true;
        };

        constexpr auto iconNoEmergency = "emergency_W_G";
        auto textNoEmergency           = utils::localize.get("app_call_wrong_emergency");
        constexpr auto iconNoSim       = "info_big_circle_W_G";
        const auto textNoSim           = utils::localize.get("app_call_no_sim");

        auto action = actionMsg->toAction();

        switch (const auto actionId = action->getAction(); actionId) {
        case app::manager::actions::CallRejectNotEmergency:
            utils::findAndReplaceAll(textNoEmergency, "$NUMBER", action->getData()->getDescription());
        addActionReceiver(manager::actions::NotAnEmergencyNotification, [this](auto &&data) {
            auto buttonAction = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            constexpr auto iconNoEmergency = "emergency_W_G";
            auto textNoEmergency           = utils::localize.get("app_call_wrong_emergency");
            utils::findAndReplaceAll(textNoEmergency, "$NUMBER", data->getDescription());
            showNotification(buttonAction, iconNoEmergency, textNoEmergency);
            break;
        case app::manager::actions::CallRejectNoSim:
            return msgHandled();
        });
        addActionReceiver(manager::actions::NoSimNotification, [this](auto &&data) {
            auto buttonAction = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            constexpr auto iconNoSim = "info_big_circle_W_G";
            const auto textNoSim     = utils::localize.get("app_call_no_sim");
            showNotification(buttonAction, iconNoSim, textNoSim);
            break;
        }
        return std::make_shared<sys::ResponseMessage>();
            return msgHandled();
        });
    }

    //  number of seconds after end call to switch back to previous application


@@ 99,14 86,34 @@ namespace app
        manager::Controller::sendAction(this, manager::actions::Call, std::make_unique<app::CallActiveData>());
    }

    void ApplicationCall::IncomingCallHandler(const CellularCallMessage *const msg)
    void ApplicationCall::CallerIdHandler(const CellularCallMessage *const msg)
    {
        if (state == call::State::IDLE) {
        if (getState() == call::State::IDLE) {
            if (callerIdTimer) {
                callerIdTimer->stop();
                callerIdTimer.reset(nullptr);
            }
            manager::Controller::sendAction(
                this, manager::actions::Call, std::make_unique<app::IncomingCallData>(msg->number));
        }
    }

    void ApplicationCall::IncomingCallHandler(const CellularCallMessage *const msg)
    {
        if (getState() == call::State::IDLE) {
            constexpr sys::ms callerIdTimeout = 1000;
            callerIdTimer =
                std::make_unique<sys::Timer>("CallerIdTimer", this, callerIdTimeout, sys::Timer::Type::SingleShot);
            callerIdTimer->connect([=](sys::Timer &) {
                callerIdTimer->stop();
                manager::Controller::sendAction(
                    this,
                    manager::actions::Call,
                    std::make_unique<app::IncomingCallData>(utils::PhoneNumber().getView()));
            });
        }
    }

    void ApplicationCall::RingingHandler(const CellularCallMessage *const msg)
    {
        manager::Controller::sendAction(


@@ 153,6 160,9 @@ namespace app
            case CellularCallMessage::Type::IncomingCall: {
                IncomingCallHandler(msg);
            } break;
            case CellularCallMessage::Type::CallerId: {
                CallerIdHandler(msg);
            } break;
            }
        }


M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +7 -2
@@ 32,6 32,7 @@ namespace app
    class CallWindowInterface
    {
      public:
        using NumberChangeCallback              = std::function<void(utils::PhoneNumber::View)>;
        virtual ~CallWindowInterface() noexcept = default;

        virtual void setState(app::call::State state) noexcept              = 0;


@@ 70,8 71,10 @@ namespace app
        void CallAbortHandler();
        void CallActiveHandler();
        void IncomingCallHandler(const CellularCallMessage *const msg);
        void CallerIdHandler(const CellularCallMessage *const msg);
        void RingingHandler(const CellularCallMessage *const msg);
        auto HandleMessageAsAction(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>;

        std::unique_ptr<sys::Timer> callerIdTimer;

      protected:
        call::State state = call::State::IDLE;


@@ 124,7 127,9 @@ namespace app
            return {{manager::actions::Launch,
                     manager::actions::Call,
                     manager::actions::Dial,
                     manager::actions::EmergencyDial}};
                     manager::actions::EmergencyDial,
                     manager::actions::NotAnEmergencyNotification,
                     manager::actions::NoSimNotification}};
        }
    };
} /* namespace app */

M module-apps/application-call/data/CallAppStyle.hpp => module-apps/application-call/data/CallAppStyle.hpp +1 -0
@@ 25,6 25,7 @@ namespace callAppStyle
        inline constexpr auto speaker   = "app_call_speaker";
        inline constexpr auto speakeron = "app_call_speaker_on";
        inline constexpr auto bluetooth = "app_call_bluetooth";
        inline constexpr auto privateNumber = "app_call_private_number";
    } // namespace strings

    namespace numberLabel

M module-apps/application-call/windows/CallWindow.cpp => module-apps/application-call/windows/CallWindow.cpp +31 -17
@@ 162,15 162,22 @@ namespace gui
        switch (state) {
        case State::INCOMING_CALL: {
            interface->startAudioRinging();
            bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
            bottomBar->setText(gui::BottomBar::Side::LEFT, utils::localize.get(strings::answer), true);
            bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::localize.get(strings::reject), true);
            durationLabel->setText(utils::localize.get(strings::iscalling));
            durationLabel->setVisible(true);
            sendSmsIcon->setVisible(true);
            speakerIcon->setVisible(false);
            microphoneIcon->setVisible(false);
            setFocusItem(sendSmsIcon);
            if (phoneNumber.getFormatted().empty()) {
                bottomBar->setActive(gui::BottomBar::Side::CENTER, false);
                sendSmsIcon->setVisible(false);
                setFocusItem(nullptr);
            }
            else {
                bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
                sendSmsIcon->setVisible(true);
                setFocusItem(sendSmsIcon);
            }
        } break;
        case State::CALL_ENDED: {
            interface->stopAudio();


@@ 222,6 229,7 @@ namespace gui
            stopCallTimer();
            [[fallthrough]];
        default:
            numberLabel->clear();
            bottomBar->setActive(gui::BottomBar::Side::LEFT, false);
            bottomBar->setActive(gui::BottomBar::Side::CENTER, false);
            bottomBar->setActive(gui::BottomBar::Side::RIGHT, false);


@@ 249,22 257,27 @@ namespace gui
    void CallWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (auto callData = dynamic_cast<app::CallSwitchData *>(data); callData != nullptr) {
            phoneNumber      = callData->getPhoneNumber();
            auto contact     = DBServiceAPI::MatchContactByPhoneNumber(this->application, phoneNumber);
            auto displayName = phoneNumber.getFormatted();
            if (contact) {
                LOG_INFO("number = %s recognized as contact id = %" PRIu32 ", name = %s",
                         phoneNumber.getEntered().c_str(),
                         contact->ID,
                         contact->getFormattedName().c_str());
                displayName = contact->getFormattedName();
            phoneNumber = callData->getPhoneNumber();
            if (!callData->getPhoneNumber().getFormatted().empty()) {
                auto contact     = DBServiceAPI::MatchContactByPhoneNumber(this->application, phoneNumber);
                auto displayName = phoneNumber.getFormatted();
                if (contact) {
                    LOG_INFO("number = %s recognized as contact id = %" PRIu32 ", name = %s",
                             phoneNumber.getEntered().c_str(),
                             contact->ID,
                             contact->getFormattedName().c_str());
                    displayName = contact->getFormattedName();
                }
                else {
                    LOG_INFO("number = %s was not recognized as any valid contact", phoneNumber.getEntered().c_str());
                }

                numberLabel->setText(displayName);
            }
            else {
                LOG_INFO("number = %s was not recognized as any valid contact", phoneNumber.getEntered().c_str());
                numberLabel->setText(utils::localize.get(strings::privateNumber));
            }

            numberLabel->setText(displayName);

            if (dynamic_cast<app::IncomingCallData *>(data) != nullptr) {
                if (getState() == State::INCOMING_CALL) {
                    LOG_DEBUG("ignoring IncomingCallData message");


@@ 317,8 330,8 @@ namespace gui
        case State::CALL_IN_PROGRESS:
            interface->hangupCall();
            return true;
            break;
        default:
        case State::IDLE:
        case State::CALL_ENDED:
            break;
        }



@@ 411,4 424,5 @@ namespace gui
        callDuration = std::chrono::seconds().zero();
        stop_timer   = true;
    }

} /* namespace gui */

M module-apps/application-calllog/CalllogModel.cpp => module-apps/application-calllog/CalllogModel.cpp +26 -11
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalllogModel.hpp"


@@ 61,20 61,11 @@ gui::ListItem *CalllogModel::getItem(gui::Order order)

    auto item = new gui::CalllogItem(this, !(application->isTimeFormat12()));

    item->setCall(call);
    item->activatedCallback = [=](gui::Item &item) {
        LOG_INFO("activatedCallback");
        std::unique_ptr<gui::SwitchData> data = std::make_unique<calllog::CallLogSwitchData>(*call);
        application->switchWindow(calllog::settings::DetailsWindowStr, std::move(data));
        return true;
    };

    item->inputCallback = [this, item](gui::Item &, const gui::InputEvent &event) {
    auto callCallback = [this, item](gui::Item &, const gui::InputEvent &event) {
        if (event.state != gui::InputEvent::State::keyReleasedShort) {
            return false;
        }
        if (event.keyCode == gui::KeyCode::KEY_LF) {
            LOG_DEBUG("calling");
            return app::manager::Controller::sendAction(
                application,
                app::manager::actions::Dial,


@@ 82,5 73,29 @@ gui::ListItem *CalllogModel::getItem(gui::Order order)
        }
        return false;
    };

    item->focusChangedCallback = [=](gui::Item &item) {
        if (item.focus == true) {
            auto &callLogItem = dynamic_cast<gui::CalllogItem &>(item);
            if (callLogItem.getCall().presentation == PresentationType::PR_UNKNOWN) {
                // disable call button since the number is unknown
                application->getCurrentWindow()->setBottomBarActive(gui::BottomBar::Side::LEFT, false);
                item.inputCallback = nullptr;
            }
            else {
                application->getCurrentWindow()->setBottomBarActive(gui::BottomBar::Side::LEFT, true);
                item.inputCallback = callCallback;
            }
        }
        return true;
    };

    item->setCall(call);
    item->activatedCallback = [=](gui::Item &item) {
        std::unique_ptr<gui::SwitchData> data = std::make_unique<calllog::CallLogSwitchData>(*call);
        application->switchWindow(calllog::settings::DetailsWindowStr, std::move(data));
        return true;
    };

    return item;
}

M module-apps/application-calllog/data/CallLogStyle.hpp => module-apps/application-calllog/data/CallLogStyle.hpp +5 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 7,6 7,10 @@
// CALL LOG STYLE
namespace callLogStyle
{
    namespace strings
    {
        inline constexpr auto privateNumber = "app_call_private_number";
    }
    // DETAILS WINDOW
    namespace detailsWindow
    {

M module-apps/application-calllog/widgets/CalllogItem.cpp => module-apps/application-calllog/widgets/CalllogItem.cpp +6 -1
@@ 63,7 63,12 @@ namespace gui
    void CalllogItem::setCall(std::shared_ptr<CalllogRecord> &call)
    {
        this->call = call;
        text->setText(call->name);
        if (call->presentation == PresentationType::PR_UNKNOWN) {
            text->setText(utils::localize.get(callLogStyle::strings::privateNumber));
        }
        else {
            text->setText(call->name);
        }

        auto callType = calllog::toCallLogCallType(call->type);
        if (callType == calllog::CallLogCallType::MISSED) {

M module-apps/application-calllog/windows/CallLogDetailsWindow.cpp => module-apps/application-calllog/windows/CallLogDetailsWindow.cpp +15 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CallLogDetailsWindow.hpp"


@@ 206,8 206,6 @@ namespace gui

            setTitle(record.name);

            number->setText(record.phoneNumber.getFormatted());

            auto callType = toCallLogCallType(record.type);
            for (auto &img : callTypeImg) {
                img->setVisible(false);


@@ 243,6 241,20 @@ namespace gui

        if (mode == ShowMode::GUI_SHOW_INIT)
            setFocusItem(rects[static_cast<uint32_t>(FocusRects::Call)]);

        if (record.presentation == PresentationType::PR_UNKNOWN) {
            rects[FocusRects::Call]->setVisible(false);
            rects[FocusRects::Call]->activatedCallback    = nullptr;
            rects[FocusRects::Call]->focusChangedCallback = nullptr;
            rects[FocusRects::Sms]->setVisible(false);
            rects[FocusRects::Sms]->activatedCallback    = nullptr;
            rects[FocusRects::Sms]->focusChangedCallback = nullptr;
            number->setText(utils::localize.get(callLogStyle::strings::privateNumber));
            bottomBar->setActive(BottomBar::Side::CENTER, false);
        }
        else {
            number->setText(record.phoneNumber.getFormatted());
        }
    }

    bool CallLogDetailsWindow::onInput(const InputEvent &inputEvent)

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

#include "CallLogMainWindow.hpp"


@@ 13,6 13,7 @@
#include <Label.hpp>
#include <Margins.hpp>
#include <Style.hpp>
#include <InputEvent.hpp>

#include <cassert>
#include <functional>


@@ 75,8 76,10 @@ namespace gui

    bool CallLogMainWindow::onDatabaseMessage(sys::Message *msgl)
    {
        DBCalllogResponseMessage *msg = reinterpret_cast<DBCalllogResponseMessage *>(msgl);
        return calllogModel->updateRecords(std::move(*msg->records));
        auto *msg = dynamic_cast<DBCalllogResponseMessage *>(msgl);
        if (msg != nullptr) {
            return calllogModel->updateRecords(std::move(*msg->records));
        }
        return false;
    }

} /* namespace gui */

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

#pragma once

M module-apps/application-calllog/windows/CallLogOptionsWindow.cpp => module-apps/application-calllog/windows/CallLogOptionsWindow.cpp +13 -12
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CallLogOptionsWindow.hpp"


@@ 10,19 10,20 @@

std::list<gui::Option> calllogWindowOptions(app::ApplicationCallLog *app, const CalllogRecord &record)
{
    auto searchResults = DBServiceAPI::ContactGetByIDWithTemporary(app, record.getContactId());

    std::list<gui::Option> options;
    if (record.presentation != PresentationType::PR_UNKNOWN) {
        auto searchResults = DBServiceAPI::ContactGetByIDWithTemporary(app, record.getContactId());

    if (searchResults->empty() || !searchResults->front().isValid() || searchResults->front().isTemporary()) {
        // add option - add contact
        options.emplace_back(gui::Option{
            std::make_unique<gui::option::Contact>(app, gui::option::ContactOperation::Add, searchResults->front())});
    }
    else {
        // add option - contact details
        options.emplace_back(gui::Option{std::make_unique<gui::option::Contact>(
            app, gui::option::ContactOperation::Details, searchResults->front())});
        if (searchResults->empty() || !searchResults->front().isValid() || searchResults->front().isTemporary()) {
            // add option - add contact
            options.emplace_back(gui::Option{std::make_unique<gui::option::Contact>(
                app, gui::option::ContactOperation::Add, searchResults->front())});
        }
        else {
            // add option - contact details
            options.emplace_back(gui::Option{std::make_unique<gui::option::Contact>(
                app, gui::option::ContactOperation::Details, searchResults->front())});
        }
    }

    // add option delete call option

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

#pragma once

M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 34,6 34,7 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCpin.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcQiurc.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcPoweredDown.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcRing.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcResponse.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcFactory.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/Commands.cpp

M module-cellular/at/UrcHandler.hpp => module-cellular/at/UrcHandler.hpp +3 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 13,6 13,7 @@ namespace at::urc
    class Qind;
    class Cpin;
    class Qiurc;
    class Ring;
    class PoweredDown;
    class UrcResponse;



@@ 27,6 28,7 @@ namespace at::urc
        virtual void Handle(Qind &urc)        = 0;
        virtual void Handle(Cpin &urc)        = 0;
        virtual void Handle(Qiurc &urc)       = 0;
        virtual void Handle(Ring &urc)        = 0;
        virtual void Handle(PoweredDown &urc) = 0;
        virtual void Handle(UrcResponse &urc) = 0;
    };

A module-cellular/at/UrcRing.hpp => module-cellular/at/UrcRing.hpp +42 -0
@@ 0,0 1,42 @@
// 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 "Urc.hpp"

namespace at::urc
{
    class Ring : public Urc
    {
      public:
        enum RingType
        {
            Normal,
            Async,
            Sync,
            RelAsync,
            RelSync,
            Fax,
            Voice
        };

        static constexpr std::string_view headUnsolicited = "+CRING";
        static constexpr std::string_view headNormal      = "RING";

        static auto isURC(const std::string &uHead) -> bool
        {
            return uHead.find(Ring::headUnsolicited) == 0 || uHead.find(Ring::headNormal) == 0;
        }

        using Urc::Urc;

        [[nodiscard]] auto isValid() const noexcept -> bool override;
        [[nodiscard]] auto getType() const -> std::optional<enum RingType>;

        void Handle(UrcHandler &h) final
        {
            h.Handle(*this);
        }
    };
} // namespace at::urc

M module-cellular/at/src/Urc.cpp => module-cellular/at/src/Urc.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <Urc.hpp>


@@ 6,7 6,7 @@

namespace at::urc
{
    Urc::Urc(const std::string &urcBody, const std::string &urcHead) : urcBody(urcBody), urcHead(urcHead)
    Urc::Urc(const std::string &urcBody, const std::string &urcHead) : urcBody(utils::trim(urcBody)), urcHead(urcHead)
    {
        split(urcBody);
    }

M module-cellular/at/src/UrcFactory.cpp => module-cellular/at/src/UrcFactory.cpp +7 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <UrcFactory.hpp>


@@ 13,6 13,8 @@
#include <UrcResponse.hpp>
#include <UrcCpin.hpp>
#include <UrcQiurc.hpp>
#include <UrcRing.hpp>

using namespace at::urc;

std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)


@@ 22,7 24,7 @@ std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)
    }
    const char headDelimiter = ':';
    auto it                  = std::find(urcMessage.begin(), urcMessage.end(), headDelimiter);
    std::string head         = std::string(urcMessage.begin(), it);
    std::string head         = utils::trim(std::string(urcMessage.begin(), it));
    std::string body         = std::string(it == urcMessage.end() ? urcMessage.begin() : it + 1, urcMessage.end());

    if (Ctze::isURC(head)) {


@@ 49,6 51,9 @@ std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)
    else if (PoweredDown::isURC(head)) {
        return std::make_unique<PoweredDown>(body);
    }
    else if (Ring::isURC(head)) {
        return std::make_unique<Ring>(body);
    }
    else if (auto type = UrcResponse::isURC(head)) {
        return std::make_unique<UrcResponse>(type.value());
    }

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

#include <UrcRing.hpp>

auto at::urc::Ring::isValid() const noexcept -> bool
{
    if (getType() == std::nullopt) {
        return false;
    }
    return Urc::isValid();
}

auto at::urc::Ring::getType() const -> std::optional<at::urc::Ring::RingType>
{
    if (urcBody == at::urc::Ring::headNormal) {
        return at::urc::Ring::RingType::Normal;
    }
    else if (urcBody == "ASYNC") {
        return at::urc::Ring::RingType::Async;
    }
    else if (urcBody == "SYNC") {
        return at::urc::Ring::RingType::Sync;
    }
    else if (urcBody == "REL ASYNC") {
        return at::urc::Ring::RingType::RelAsync;
    }
    else if (urcBody == "REL SYNC") {
        return at::urc::Ring::RingType::RelSync;
    }
    else if (urcBody == "FAX") {
        return at::urc::Ring::RingType::Fax;
    }
    else if (urcBody == "VOICE") {
        return at::urc::Ring::RingType::Voice;
    }

    return std::nullopt;
}

M module-cellular/test/unittest_URC.cpp => module-cellular/test/unittest_URC.cpp +36 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "Utils.hpp"


@@ 18,6 18,7 @@
#include "UrcClip.hpp"
#include "UrcCpin.hpp"
#include "UrcQiurc.hpp"
#include "UrcRing.hpp"
#include "UrcPoweredDown.hpp"
#include "UrcResponse.hpp"
#include "UrcFactory.hpp"


@@ 753,11 754,6 @@ TEST_CASE("Urc RESPONSE")
        REQUIRE(rsp);
        REQUIRE(rsp->getURCResponseType() == at::urc::UrcResponse::URCResponseType::Connect);

        urc = at::urc::UrcFactory::Create("RING");
        rsp = getURC<at::urc::UrcResponse>(urc);
        REQUIRE(rsp);
        REQUIRE(rsp->getURCResponseType() == at::urc::UrcResponse::URCResponseType::Ring);

        urc = at::urc::UrcFactory::Create("NO CARRIER");
        rsp = getURC<at::urc::UrcResponse>(urc);
        REQUIRE(rsp);


@@ 841,3 837,37 @@ TEST_CASE("+Qiurc: TCP Context and connection message")
        REQUIRE(qiurc->getType() == std::nullopt);
    }
}

TEST_CASE("RING")
{
    struct RingTest
    {
        std::string body;
        std::optional<at::urc::Ring::RingType> ringType;
    };

    std::vector<RingTest> testCases = {
        {"RING", at::urc::Ring::Normal},
        {"+CRING: ASYNC", at::urc::Ring::Async},
        {"+CRING: SYNC", at::urc::Ring::Sync},
        {"+CRING: REL ASYNC", at::urc::Ring::RelAsync},
        {"\r\n+CRING: REL SYNC", at::urc::Ring::RelSync},
        {"+CRING: FAX", at::urc::Ring::Fax},
        {"+CRING: VOICE", at::urc::Ring::Voice},
        {"+CRING: BUMMER", std::nullopt},
    };

    for (auto &test : testCases) {
        auto urc  = at::urc::UrcFactory::Create(test.body);
        auto ring = getURC<at::urc::Ring>(urc);
        REQUIRE(ring);
        if (test.ringType) {
            REQUIRE(ring->getType());
            REQUIRE(ring->getType().value() == test.ringType);
        }
        else {
            REQUIRE(!ring->isValid());
            REQUIRE(!ring->getType());
        }
    }
}

M module-db/Interface/CalllogRecord.cpp => module-db/Interface/CalllogRecord.cpp +49 -21
@@ 47,19 47,29 @@ CalllogRecordInterface::CalllogRecordInterface(CalllogDB *calllogDb, ContactsDB 

bool CalllogRecordInterface::Add(const CalllogRecord &rec)
{
    ContactRecordInterface contactInterface(contactsDB);
    auto contactMatch =
        contactInterface.MatchByNumber(rec.phoneNumber, ContactRecordInterface::CreateTempContact::True);
    if (!contactMatch) {
        LOG_ERROR("Cannot get contact, for number %s", rec.phoneNumber.getNonEmpty().c_str());
        return false;
    }
    auto &contactRec = contactMatch->contact;

    auto localRec      = rec;
    localRec.contactId = contactRec.ID;
    localRec.name      = contactRec.getFormattedName();
    LOG_DEBUG("Adding calllog record %s", utils::to_string(localRec).c_str());
    if (!rec.phoneNumber.getFormatted().empty()) {
        ContactRecordInterface contactInterface(contactsDB);
        auto contactMatch =
            contactInterface.MatchByNumber(rec.phoneNumber, ContactRecordInterface::CreateTempContact::True);
        if (!contactMatch) {
            LOG_ERROR("Cannot get contact, for number %s", rec.phoneNumber.getNonEmpty().c_str());
            return false;
        }
        auto &contactRec = contactMatch->contact;

        localRec.contactId = contactRec.ID;
        localRec.name      = contactRec.getFormattedName();
        if (localRec.presentation == PresentationType::PR_UNKNOWN) {
            localRec.presentation = PresentationType::PR_ALLOWED;
        }
        LOG_DEBUG("Adding call log record %s", utils::to_string(localRec).c_str());
    }
    else {
        // private number so do not add contact just call log entry
        localRec.presentation = PresentationType::PR_UNKNOWN;
        LOG_DEBUG("Adding private call entry to call log record.");
    }

    return calllogDB->calls.add(CalllogTableRow{{.ID = localRec.ID}, // this is only to remove warning
                                                .number       = localRec.phoneNumber.getEntered(),


@@ 71,7 81,6 @@ bool CalllogRecordInterface::Add(const CalllogRecord &rec)
                                                .name         = localRec.name,
                                                .contactId    = localRec.contactId,
                                                .isRead       = localRec.isRead});
    ;
}

uint32_t CalllogRecordInterface::GetLastID()


@@ 119,21 128,37 @@ std::unique_ptr<std::vector<CalllogRecord>> CalllogRecordInterface::GetLimitOffs

bool CalllogRecordInterface::Update(const CalllogRecord &rec)
{

    auto call = calllogDB->calls.getById(rec.ID);
    if (!call.isValid()) {
        return false;
    }

    auto contactID    = rec.contactId;
    auto presentation = rec.presentation;
    if (call.presentation == PresentationType::PR_UNKNOWN && rec.phoneNumber.isValid()) {
        // entry added as private was updated with phone number
        ContactRecordInterface contactInterface(contactsDB);
        auto contactMatch =
            contactInterface.MatchByNumber(rec.phoneNumber, ContactRecordInterface::CreateTempContact::True);
        if (!contactMatch) {
            LOG_ERROR("Cannot get or create temporary contact for number %s", rec.phoneNumber.getNonEmpty().c_str());
            return false;
        }
        contactID = contactMatch->contact.ID;
        if (presentation == PresentationType::PR_UNKNOWN) {
            presentation = PresentationType::PR_ALLOWED;
        }
    }

    return calllogDB->calls.update(CalllogTableRow{{.ID = rec.ID},
                                                   .number       = rec.phoneNumber.getEntered(),
                                                   .e164number   = rec.phoneNumber.getE164(),
                                                   .presentation = rec.presentation,
                                                   .presentation = presentation,
                                                   .date         = rec.date,
                                                   .duration     = rec.duration,
                                                   .type         = rec.type,
                                                   .name         = rec.name,
                                                   .contactId    = rec.contactId,
                                                   .contactId    = contactID,
                                                   .isRead       = rec.isRead});
}



@@ 165,13 190,16 @@ CalllogRecord CalllogRecordInterface::GetByID(uint32_t id)
{
    auto call = calllogDB->calls.getById(id);

    auto contactRec = GetContactRecordByID(call.contactId);
    if (contactRec.ID == DB_ID_NONE) {
        LOG_ERROR("Cannot find contact for ID %" PRIu32, call.contactId);
        return CalllogRecord();
    if (call.contactId != DB_ID_NONE) {
        auto contactRec = GetContactRecordByID(call.contactId);
        if (contactRec.ID == DB_ID_NONE) {
            LOG_ERROR("Cannot find contact for ID %" PRIu32, call.contactId);
            return CalllogRecord();
        }

        call.name = contactRec.getFormattedName();
    }

    call.name = contactRec.getFormattedName();
    return CalllogRecord(call);
}


M module-services/service-appmgr/model/ApplicationManager.cpp => module-services/service-appmgr/model/ApplicationManager.cpp +2 -0
@@ 295,6 295,8 @@ namespace app::manager
        connect(typeid(CellularMMIResultMessage), convertibleToActionHandler);
        connect(typeid(CellularMMIResponseMessage), convertibleToActionHandler);
        connect(typeid(CellularMMIPushMessage), convertibleToActionHandler);
        connect(typeid(CellularNoSimNotification), convertibleToActionHandler);
        connect(typeid(CellularNotAnEmergencyNotification), convertibleToActionHandler);
        connect(typeid(sys::CriticalBatteryLevelNotification), convertibleToActionHandler);
        connect(typeid(sys::SystemBrownoutMesssage), convertibleToActionHandler);
    }

M module-services/service-appmgr/service-appmgr/Actions.hpp => module-services/service-appmgr/service-appmgr/Actions.hpp +2 -2
@@ 26,8 26,8 @@ namespace app::manager
            Launch,
            CloseSystem,
            Call,
            CallRejectNotEmergency,
            CallRejectNoSim,
            NotAnEmergencyNotification,
            NoSimNotification,
            Dial,
            EmergencyDial,
            ShowCallLog,

M module-services/service-cellular/CellularCall.cpp => module-services/service-cellular/CellularCall.cpp +8 -2
@@ 29,8 29,8 @@ namespace CellularCall
        CalllogRecord callRec;
        callRec.type        = type;
        callRec.date        = utils::time::Timestamp().getTime();
        callRec.name        = number.getFormatted(); // temporary set name to entered number
        callRec.phoneNumber = number;
        callRec.presentation = PresentationType::PR_UNKNOWN;
        callRec.phoneNumber  = number;
        call                = startCallAction ? startCallAction(callRec) : CalllogRecord();
        if (!call.isValid()) {
            LOG_ERROR("startCallAction failed");


@@ 95,4 95,10 @@ namespace CellularCall

        return true;
    }

    void CellularCall::setNumber(const utils::PhoneNumber::View &number)
    {
        call.presentation = number.getFormatted().empty() ? PresentationType::PR_UNKNOWN : PresentationType::PR_ALLOWED;
        call.phoneNumber  = number;
    }
} // namespace CellularCall

M module-services/service-cellular/CellularRequestHandler.cpp => module-services/service-cellular/CellularRequestHandler.cpp +14 -0
@@ 19,6 19,7 @@
#include "service-cellular/requests/ClirRequest.hpp"
#include "service-cellular/requests/ClipRequest.hpp"
#include "service-cellular/requests/CallWaitingRequest.hpp"
#include "service-cellular/requests/RejectRequest.hpp"

#include <service-appmgr/model/ApplicationManager.hpp>



@@ 72,6 73,19 @@ void CellularRequestHandler::handle(cellular::CallRequest &request, at::Result &
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::RejectRequest &request, at::Result &result)
{
    if (request.getRejectReason() == cellular::RejectRequest::RejectReason::NoSim) {
        auto message = std::make_shared<CellularNoSimNotification>(request.getNumber());
        cellular.bus.sendUnicast(message, app::manager::ApplicationManager::ServiceName);
    }
    else if (request.getRejectReason() == cellular::RejectRequest::RejectReason::NotAnEmergencyNumber) {
        auto message = std::make_shared<CellularNotAnEmergencyNotification>(request.getNumber());
        cellular.bus.sendUnicast(message, app::manager::ApplicationManager::ServiceName);
    }
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::SupplementaryServicesRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);

M module-services/service-cellular/CellularUrcHandler.cpp => module-services/service-cellular/CellularUrcHandler.cpp +8 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CellularUrcHandler.hpp"


@@ 25,8 25,13 @@ void CellularUrcHandler::Handle(Clip &urc)
    if (urc.isValid()) {
        phoneNumber = urc.getNumber();
    }
    // continue without number
    response = std::make_unique<CellularCallMessage>(CellularCallMessage::Type::IncomingCall, phoneNumber);
    response = std::make_unique<CellularCallMessage>(CellularCallMessage::Type::CallerId, phoneNumber);
    urc.setHandled(true);
}

void CellularUrcHandler::Handle(Ring &urc)
{
    response = std::make_unique<CellularCallMessage>(CellularCallMessage::Type::IncomingCall, "");
    urc.setHandled(true);
}


M module-services/service-cellular/CellularUrcHandler.hpp => module-services/service-cellular/CellularUrcHandler.hpp +2 -0
@@ 17,6 17,7 @@
#include <module-cellular/at/UrcQind.hpp>
#include <module-cellular/at/UrcResponse.hpp>
#include <module-cellular/at/UrcQiurc.hpp>
#include <module-cellular/at/UrcRing.hpp>

/**
 * ServiceCellular helper for handling Urc messages


@@ 35,6 36,7 @@ class CellularUrcHandler : public at::urc::UrcHandler
    void Handle(at::urc::Qind &urc) final;
    void Handle(at::urc::Cpin &urc) final;
    void Handle(at::urc::Qiurc &urc) final;
    void Handle(at::urc::Ring &urc) final;
    void Handle(at::urc::PoweredDown &urc) final;
    void Handle(at::urc::UrcResponse &urc) final;


M module-services/service-cellular/RequestFactory.cpp => module-services/service-cellular/RequestFactory.cpp +4 -13
@@ 14,6 14,7 @@
#include "service-cellular/requests/PinChangeRequest.hpp"
#include "service-cellular/requests/ImeiRequest.hpp"
#include "service-cellular/requests/UssdRequest.hpp"
#include "service-cellular/requests/RejectRequest.hpp"

#include <common_data/EventStore.hpp>
#include <cmd/QECCNUM.hpp>


@@ 57,11 58,11 @@ namespace cellular
                return std::make_unique<CallRequest>(request);
            }
            else {
                actionRequest = app::manager::actions::Action::CallRejectNoSim;
                return std::make_unique<RejectRequest>(RejectRequest::RejectReason::NoSim, request);
            }
        }
        else if (requestMode == CellularCallRequestMessage::RequestMode::Emergency) {
            actionRequest = app::manager::actions::Action::CallRejectNotEmergency;
            return std::make_unique<RejectRequest>(RejectRequest::RejectReason::NotAnEmergencyNumber, request);
        }
        return nullptr;
    }


@@ 72,10 73,6 @@ namespace cellular
            return req;
        }

        if (actionRequest) {
            return nullptr;
        }

        std::string groupA, groupB, groupC, groupD, groupE, groupF;
        GroupMatch matchPack = {groupA, groupB, groupC, groupD, groupE, groupF};



@@ 120,15 117,9 @@ namespace cellular
        }

        if (simStatus == SimStatus::SimSlotEmpty) {
            actionRequest = app::manager::actions::Action::CallRejectNoSim;
            return nullptr;
            return std::make_unique<RejectRequest>(RejectRequest::RejectReason::NoSim, request);
        }
        return std::make_unique<CallRequest>(request);
    }

    std::optional<app::manager::actions::Action> &RequestFactory::getActionRequest()
    {
        return actionRequest;
    }

} // namespace cellular

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +8 -9
@@ 768,6 768,10 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
            responseMsg = std::make_shared<CellularResponseMessage>(ret);
            break;
        }
        case CellularCallMessage::Type::CallerId: {
            ongoingCall.setNumber(msg->number);
            break;
        }
        }
    } break;
    // Incoming notifications from Notification Virtual Channel


@@ 932,15 936,10 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,

        auto request = factory.create();

        if (auto action = factory.getActionRequest(); action) {
            responseMsg = std::make_shared<CellularActionResponseMessage>(action.value(), msg->number.getEntered());
        }
        else {
            CellularRequestHandler handler(*this);
            auto result = channel->cmd(request->command(), at::default_doc_timeout);
            request->handle(handler, result);
            responseMsg = std::make_shared<CellularResponseMessage>(request->isHandled());
        }
        CellularRequestHandler handler(*this);
        auto result = channel->cmd(request->command(), at::default_doc_timeout);
        request->handle(handler, result);
        responseMsg = std::make_shared<CellularResponseMessage>(request->isHandled());

    } break;
    case MessageType::DBServiceNotification: {

M module-services/service-cellular/service-cellular/CellularCall.hpp => module-services/service-cellular/service-cellular/CellularCall.hpp +3 -2
@@ 8,6 8,7 @@
#include <Tables/CalllogTable.hpp>
#include <time/time_conversion.hpp>
#include <utf8/UTF8.hpp>
#include <Utils.hpp>

#include <cstdint>
#include <functional>


@@ 73,10 74,10 @@ namespace CellularCall
            endCallAction = callAction;
        }

        bool startCall(const utils::PhoneNumber::View &number, const CallType type);

        bool setActive();
        void setNumber(const utils::PhoneNumber::View &number);

        bool startCall(const utils::PhoneNumber::View &number, const CallType type);
        bool endCall(Forced forced = Forced::False);

        bool isValid() const

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +33 -17
@@ 37,6 37,7 @@ class CellularCallMessage : public CellularMessage
    {
        Ringing,      // user provided number to call to and service initialized calling procedure.
        IncomingCall, // device receives connection from other device.
        CallerId,     // device receives caller id
    };

    CellularCallMessage() = delete;


@@ 626,23 627,6 @@ class CellularResponseMessage : public sys::ResponseMessage
    std::string data;
};

class CellularActionResponseMessage : public CellularResponseMessage, public app::manager::actions::ConvertibleToAction
{
  public:
    CellularActionResponseMessage(app::manager::actions::ActionId actionId, std::string data)
        : CellularResponseMessage(false, data), actionId(actionId)
    {}

    [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
    {
        return std::make_unique<app::manager::ActionRequest>(
            sender, actionId, std::make_unique<app::manager::actions::ActionParams>(data));
    }

  private:
    const app::manager::actions::ActionId actionId;
};

class CellularAntennaResponseMessage : public sys::ResponseMessage
{
  public:


@@ 760,6 744,38 @@ class CellularCheckIfStartAllowedMessage : public sys::Message
    CellularCheckIfStartAllowedMessage() : sys::Message()
    {}
};

class CellularNoSimNotification : public CellularResponseMessage, public app::manager::actions::ConvertibleToAction
{
  public:
    CellularNoSimNotification(std::string data) : CellularResponseMessage(false, data)
    {}

    [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
    {
        return std::make_unique<app::manager::ActionRequest>(
            sender,
            app::manager::actions::NoSimNotification,
            std::make_unique<app::manager::actions::ActionParams>(data));
    }
};

class CellularNotAnEmergencyNotification : public CellularResponseMessage,
                                           public app::manager::actions::ConvertibleToAction
{
  public:
    CellularNotAnEmergencyNotification(std::string data) : CellularResponseMessage(false, data)
    {}

    [[nodiscard]] auto toAction() const -> std::unique_ptr<app::manager::ActionRequest>
    {
        return std::make_unique<app::manager::ActionRequest>(
            sender,
            app::manager::actions::NotAnEmergencyNotification,
            std::make_unique<app::manager::actions::ActionParams>(data));
    }
};

namespace cellular
{


M module-services/service-cellular/service-cellular/CellularRequestHandler.hpp => module-services/service-cellular/service-cellular/CellularRequestHandler.hpp +1 -0
@@ 15,6 15,7 @@ class CellularRequestHandler : public cellular::RequestHandler
    void handle(cellular::ImeiRequest &request, at::Result &result) final;
    void handle(cellular::UssdRequest &request, at::Result &result) final;
    void handle(cellular::CallRequest &request, at::Result &result) final;
    void handle(cellular::RejectRequest &request, at::Result &result) final;
    void handle(cellular::PasswordRegistrationRequest &request, at::Result &result) final;
    void handle(cellular::SupplementaryServicesRequest &request, at::Result &result) final;
    void handle(cellular::PinChangeRequest &request, at::Result &result) final;

M module-services/service-cellular/service-cellular/RequestFactory.hpp => module-services/service-cellular/service-cellular/RequestFactory.hpp +0 -2
@@ 32,7 32,6 @@ namespace cellular
                       CellularCallRequestMessage::RequestMode requestMode,
                       SimStatus simCardStatus);
        std::unique_ptr<IRequest> create();
        std::optional<app::manager::actions::Action> &getActionRequest();

      private:
        void registerRequest(std::string regex, CreateCallback);


@@ 40,7 39,6 @@ namespace cellular

        std::string request;
        std::vector<std::pair<std::string, CreateCallback>> requestMap;
        std::optional<app::manager::actions::Action> actionRequest;

        at::BaseChannel &channel;
        const CellularCallRequestMessage::RequestMode requestMode;

M module-services/service-cellular/service-cellular/RequestHandler.hpp => module-services/service-cellular/service-cellular/RequestHandler.hpp +2 -0
@@ 10,6 10,7 @@ namespace cellular
    class ImeiRequest;
    class UssdRequest;
    class CallRequest;
    class RejectRequest;
    class SupplementaryServicesRequest;
    class PasswordRegistrationRequest;
    class PinChangeRequest;


@@ 25,6 26,7 @@ namespace cellular
        virtual void handle(ImeiRequest &request, at::Result &result)                  = 0;
        virtual void handle(UssdRequest &request, at::Result &result)                  = 0;
        virtual void handle(CallRequest &request, at::Result &result)                  = 0;
        virtual void handle(RejectRequest &request, at::Result &result)                = 0;
        virtual void handle(PasswordRegistrationRequest &request, at::Result &result)  = 0;
        virtual void handle(PinChangeRequest &request, at::Result &result)             = 0;
        virtual void handle(SupplementaryServicesRequest &request, at::Result &result) = 0;

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +1 -0
@@ 189,6 189,7 @@ class ServiceCellular : public sys::Service
        PowerCycle, //<! PWRKEY pin toggle
        HardReset   //<! RESET_N pin
    };

    bool resetCellularModule(ResetType type);
    bool isAfterForceReboot = false;
    bool nextPowerStateChangeAwaiting          = false;

A module-services/service-cellular/service-cellular/requests/RejectRequest.hpp => module-services/service-cellular/service-cellular/requests/RejectRequest.hpp +48 -0
@@ 0,0 1,48 @@
// 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 <at/Result.hpp>

#include "service-cellular/RequestHandler.hpp"
#include "service-cellular/requests/Request.hpp"

namespace cellular
{
    class RejectRequest : public Request
    {
      public:
        enum class RejectReason
        {
            NoSim,
            NotAnEmergencyNumber
        };

        RejectRequest(RejectReason rejectReason, const std::string &number)
            : Request(number), rejectReason(rejectReason){};

        std::string command() final
        {
            return std::string();
        }

        void handle(RequestHandler &h, at::Result &result)
        {
            h.handle(*this, result);
        }

        RejectReason getRejectReason()
        {
            return rejectReason;
        }

        std::string getNumber() const
        {
            return request;
        }

      private:
        RejectReason rejectReason;
    };
}; // namespace cellular

M module-services/service-cellular/tests/unittest_request_factory.cpp => module-services/service-cellular/tests/unittest_request_factory.cpp +24 -18
@@ 15,6 15,7 @@
#include <service-cellular/requests/ClipRequest.hpp>
#include <service-cellular/requests/ClirRequest.hpp>
#include <service-cellular/requests/ColpRequest.hpp>
#include <service-cellular/requests/RejectRequest.hpp>

#include <module-cellular/test/mock/AtCommon_channel.hpp>



@@ 33,15 34,15 @@ TEST_CASE("Emergency handling")
        bool isNumberEmergencyNoSim;
        // mock that sim is inserted
        bool insertSim;
        // expected action returned
        std::optional<app::manager::actions::Action> expectedAction;
        // reject reason if applicable
        std::optional<RejectRequest::RejectReason> rejectReason;
        // expected typeid name of created request
        std::string expectedType;

        // test number
        std::string number = "600700800";
        // expected command crated by factory
        std::string expectedCommand = "ATD600700800;";
        // test number
        std::string number = "600700800";
    };

    std::vector<EmergencyTest> testCases = {


@@ 58,11 59,11 @@ TEST_CASE("Emergency handling")
        // no SIM emergency number / no sim inserted
        {false, false, true, false, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / no sim inserted
        {false, true, false, false, Action::CallRejectNoSim, ""},
        {false, true, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        // normal number / sim inserted
        {false, false, false, true, std::nullopt, typeid(CallRequest).name()},
        // normal number / no sim inserted
        {false, false, false, false, Action::CallRejectNoSim, ""},
        {false, false, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},

        ////// emergency request
        // no SIM and SIM emergency number / sim inserted


@@ 76,11 77,17 @@ TEST_CASE("Emergency handling")
        // no SIM emergency number / no sim inserted
        {true, false, true, false, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / no sim inserted
        {true, true, false, false, Action::CallRejectNoSim, ""},
        {true, true, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        // normal number / sim inserted
        {true, false, false, true, Action::CallRejectNotEmergency, ""},
        {true, false, false, true, RejectRequest::RejectReason::NotAnEmergencyNumber, typeid(RejectRequest).name(), ""},
        // normal number / no sim inserted
        {true, false, false, false, Action::CallRejectNotEmergency, ""},
        {true,
         false,
         false,
         false,
         RejectRequest::RejectReason::NotAnEmergencyNumber,
         typeid(RejectRequest).name(),
         ""},
    };
    int idx = 0;



@@ 108,16 115,15 @@ TEST_CASE("Emergency handling")
                                                              : CellularCallRequestMessage::RequestMode::Normal,
                                      test.insertSim ? RequestFactory::SimStatus::SimInsterted
                                                     : RequestFactory::SimStatus::SimSlotEmpty);
        auto request = requestFactory.create();
        std::shared_ptr<IRequest> request = requestFactory.create();

        if (test.expectedAction) {
            INFO("Failed test case idx: " + std::to_string(idx));
            REQUIRE(requestFactory.getActionRequest() == test.expectedAction);
            REQUIRE(request == nullptr);
        }
        else {
            REQUIRE(requestFactory.getActionRequest() == std::nullopt);
            REQUIRE(typeid(*request.get()).name() == test.expectedType);
        INFO("Failed test case idx: " + std::to_string(idx));
        REQUIRE(request != nullptr);
        REQUIRE(typeid(*request.get()).name() == test.expectedType);

        if (test.expectedType == typeid(RejectRequest).name()) {
            auto rejectRequest = std::static_pointer_cast<RejectRequest>(request);
            REQUIRE(test.rejectReason == rejectRequest->getRejectReason());
        }

        if (request) {

M module-services/service-fota/FotaUrcHandler.hpp => module-services/service-fota/FotaUrcHandler.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 25,6 25,7 @@ class FotaUrcHandler : public at::urc::UrcHandler
    virtual void Handle(at::urc::Ctze &urc){};
    virtual void Handle(at::urc::Cpin &urc){};
    virtual void Handle(at::urc::Qiurc &urc){};
    virtual void Handle(at::urc::Ring &urc){};
    virtual void Handle(at::urc::PoweredDown &urc){};
    virtual void Handle(at::urc::UrcResponse &urc){};