~aleteoryx/muditaos

05c8d3117c41c1c3881258942412de5606ff6e77 — Alek Rudnik 4 years ago d22405b
[EGD-7860] Fix for call error flow

Make sure it is not possible to request a call twice.
Added new error windows for general call error and
no network connection.
Make sure there are no issues when call is requested
from external app e.g. from callog or phonebook.
M cmake/modules/ProjectConfig.cmake => cmake/modules/ProjectConfig.cmake +1 -1
@@ 6,7 6,7 @@ else()
endif()

# add LOG_SENSITIVE_DATA enable option
option(LOG_SENSITIVE_DATA "LOG_SENSITIVE_DATA" OFF)
option(LOG_SENSITIVE_DATA "LOG_SENSITIVE_DATA" ON)
if (${LOG_SENSITIVE_DATA} STREQUAL "ON")
    set (LOG_SENSITIVE_DATA_ENABLED 1 CACHE INTERNAL "")
else()

M image/assets/lang/Deutsch.json => image/assets/lang/Deutsch.json +2 -0
@@ 313,6 313,8 @@
  "app_call_speaker_on": "LAUTSPRECHER AN",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "Keine SIM-Karte.\n\nZum Anrufen\nSIM-Karte einsetzen.",
  "app_call_no_network_connection": "Keine Netzwerkverbindung.",
  "app_call_call_request_failed": "Etwas ist schief gelaufen.",
  "app_call_offline": "Offline-Modus.\n\nZum Anrufen\n in Online-Modus wechseln.",
  "app_sms_offline": "Offline-Modus.\n\nZum Senden einer SMS\n in Online-Modus wechseln.",
  "app_call_emergency_text": "Notruf",

M image/assets/lang/English.json => image/assets/lang/English.json +2 -0
@@ 283,6 283,8 @@
  "app_call_speaker_on": "SPEAKER ON",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "No SIM.\n\nTo make a call,\nplease insert a SIM card.",
  "app_call_no_network_connection": "No Network Connection.",
  "app_call_call_request_failed": "Something went wrong.",
  "app_call_offline": "You're Offline.\n\nTo make a call,\n switch the mode.",
  "app_sms_offline": "You're Offline.\n\nTo send message,\n switch the mode.",
  "app_call_emergency_text": "Emergency call",

M image/assets/lang/Espanol.json => image/assets/lang/Espanol.json +2 -0
@@ 313,6 313,8 @@
  "app_call_speaker_on": "ALTAVOZ ACTIVADO",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "No hay SIM.\n\nPara hacer una llamada,\ndebes insertar una tarjeta SIM.",
  "app_call_no_network_connection": "No hay conexión de red.",
  "app_call_call_request_failed": "Algo salió mal.",
  "app_call_offline": "No tienes conexión.\n\nPara hacer una llamada,\n cambia al modo Conectado.",
  "app_sms_offline": "No tienes conexión.\n\nPara enviar un SMS,\n cambia al modo Conectado.",
  "app_call_emergency_text": "Llamada de emergencia",

M image/assets/lang/Francais.json => image/assets/lang/Francais.json +2 -0
@@ 282,6 282,8 @@
  "app_call_speaker_on": "HAUT-PARLEUR ACTIVÉ",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "Pas de carte SIM.\n\nPour faire un appel,\nveuillez insérer une carte SIM.",
  "app_call_no_network_connection": "Pas de connexion réseau.",
  "app_call_call_request_failed": "Quelque chose s'est mal passé.",
  "app_call_offline": "Tu es hors ligne.\n\nPour faire un appel,\npassez en mode Connecté.",
  "app_sms_offline": "Vous êtes hors ligne.\n\nPour envoyer un SMS,\npassez en mode Connecté.",
  "app_call_emergency_text": "Appel d'urgence",

M image/assets/lang/Polski.json => image/assets/lang/Polski.json +2 -0
@@ 317,6 317,8 @@
  "app_call_speaker_on": "GŁOŚNIK",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "Brak SIM.\n\nAby wykonać połączenie,\nwłóż kartę SIM.",
  "app_call_no_network_connection": "Brak połączenia sieciowego.",
  "app_call_call_request_failed": "Coś poszło nie tak.",
  "app_call_offline": "Jesteś w trybie offline.\n\nAby wykonać połączenie,\n przejdź w tryb Połączony.",
  "app_sms_offline": "Jesteś w trybie offline.\n\nAby wysłać SMS,\n przejdź w tryb Połączony.",
  "app_call_emergency_text": "Połączenie alarmowe",

M image/assets/lang/Svenska.json => image/assets/lang/Svenska.json +2 -0
@@ 257,6 257,8 @@
  "app_call_speaker_on": "HÖGT.PÅ",
  "app_call_bluetooth": "BLUETOOTH",
  "app_call_no_sim": "För att ringa,,\nsätt i ett SIM-kort.",
  "app_call_no_network_connection": "Ingen nätverksanslutning.",
  "app_call_call_request_failed": "Något gick fel.",
  "app_call_offline": "Du är frånkopplad.\n\nByt till \"ansluten\"\nför att ringa.",
  "app_call_emergency_text": "Nödsamtal",
  "app_call_wrong_emergency": "Kan inte ringa..\n$NUMBER är inte ett nödnummer.",

M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +1 -1
@@ 10,7 10,7 @@ add_library(${PROJECT_NAME} STATIC)

add_subdirectory(apps-common)

option(ENABLE_APP_ANTENNA "Enable application antenna" OFF)
option(ENABLE_APP_ANTENNA "Enable application antenna" OFF) 

set(APPLICATIONS
    alarm-clock

M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +49 -23
@@ 36,7 36,7 @@ namespace app
            {Indicator::Signal, Indicator::Time, Indicator::Battery, Indicator::SimCard});
        addActionReceiver(manager::actions::Call, [this](auto &&data) {
            if (auto msg = dynamic_cast<app::CallSwitchData *>(data.get()); msg != nullptr) {
                handleCallEvent(msg->getPhoneNumber().getEntered());
                handleCallEvent(msg->getPhoneNumber().getEntered(), ExternalRequest::True);
                return actionHandled();
            }
            return actionNotHandled();


@@ 50,34 50,29 @@ namespace app
            return actionHandled();
        });
        addActionReceiver(manager::actions::NotAnEmergencyNotification, [this](auto &&data) {
            auto buttonAction = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            constexpr auto iconNoEmergency = "fail_128px_W_G";
            auto textNoEmergency           = utils::translate("app_call_wrong_emergency");
            auto textNoEmergency = utils::translate("app_call_wrong_emergency");
            utils::findAndReplaceAll(textNoEmergency, "$NUMBER", data->getDescription());
            showNotification(buttonAction, iconNoEmergency, textNoEmergency);
            showNotificationAndRestartCallFlow(NotificationType::Info, textNoEmergency);
            return actionHandled();
        });

        addActionReceiver(manager::actions::NoSimNotification, [this](auto &&data) {
            auto buttonAction = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            constexpr auto iconNoSim = "info_128px_W_G";
            const auto textNoSim     = utils::translate("app_call_no_sim");
            showNotification(buttonAction, iconNoSim, textNoSim);
            showNotificationAndRestartCallFlow(NotificationType::Info,
                                               utils::translate("app_app_call_no_simcall_offline"));
            return actionHandled();
        });
        addActionReceiver(manager::actions::NoNetworkConnectionNotification, [this](auto &&data) {
            showNotificationAndRestartCallFlow(NotificationType::Info,
                                               utils::translate("app_call_no_network_connection"));
            return actionHandled();
        });
        addActionReceiver(manager::actions::CallRequestGeneralErrorNotification, [this](auto &&data) {
            showNotificationAndRestartCallFlow(NotificationType::Failure,
                                               utils::translate("app_call_call_request_failed"));
            return actionHandled();
        });
        addActionReceiver(manager::actions::CallRejectedByOfflineNotification, [this](auto &&data) {
            auto buttonAction = [=]() -> bool {
                app::manager::Controller::switchBack(this);
                return true;
            };
            constexpr auto icon    = "info_128px_W_G";
            const auto textOffline = utils::translate("app_call_offline");
            showNotification(buttonAction, icon, textOffline);
            showNotificationAndRestartCallFlow(NotificationType::Info, utils::translate("app_call_offline"));
            return actionHandled();
        });
        addActionReceiver(manager::actions::AbortCall, [this](auto &&data) {


@@ 112,6 107,17 @@ namespace app
        });
    }

    bool ApplicationCall::conditionalReturnToPreviousView()
    {
        // if external request simply return to previous app
        if (externalRequest == ExternalRequest::True) {
            app::manager::Controller::switchBack(this);
            return true;
        }
        returnToPreviousWindow();
        return true;
    }

    bool ApplicationCall::isPopupPermitted(gui::popup::ID popupId) const
    {
        if (popupId == gui::popup::ID::Volume) {


@@ 174,6 180,14 @@ namespace app
        return true;
    }

    auto ApplicationCall::showNotificationAndRestartCallFlow(NotificationType type, const std::string &text) -> bool
    {
        auto buttonAction = [=]() -> bool { return conditionalReturnToPreviousView(); };
        auto icon         = type == NotificationType::Info ? "info_128px_W_G" : "fail_128px_W_G";
        setCallState(call::State::IDLE);
        return showNotification(buttonAction, icon, text);
    }

    void ApplicationCall::destroyUserInterface()
    {}



@@ 206,12 220,24 @@ namespace app

    void ApplicationCall::handleEmergencyCallEvent(const std::string &number)
    {
        auto state = getCallState();
        if (state != call::State::IDLE) {
            LOG_WARN("Cannot call in %s state", c_str(state));
            return;
        }
        CellularServiceAPI::DialEmergencyNumber(this, utils::PhoneNumber(number));
    }

    void ApplicationCall::handleCallEvent(const std::string &number)
    void ApplicationCall::handleCallEvent(const std::string &number, ExternalRequest isExternalRequest)
    {
        auto state = getCallState();
        if (state != call::State::IDLE) {
            LOG_WARN("Cannot call in %s state", c_str(state));
            return;
        }
        CellularServiceAPI::DialNumber(this, utils::PhoneNumber(number));
        setCallState(call::State::OUTGOING_CALL);
        externalRequest = isExternalRequest;
    }

    void ApplicationCall::handleAddContactEvent(const std::string &number)

M module-apps/application-call/include/application-call/ApplicationCall.hpp => module-apps/application-call/include/application-call/ApplicationCall.hpp +28 -11
@@ 56,20 56,20 @@ namespace app
    class EnterNumberWindowInterface
    {
      public:
        virtual ~EnterNumberWindowInterface() noexcept                   = default;
        virtual void handleCallEvent(const std::string &number)          = 0;
        virtual void handleEmergencyCallEvent(const std::string &number) = 0;
        virtual void handleAddContactEvent(const std::string &number)    = 0;
        virtual ~EnterNumberWindowInterface() noexcept = default;
        enum class ExternalRequest
        {
            True,
            False
        };
        virtual void handleCallEvent(const std::string &number,
                                     ExternalRequest isExternalRequest = ExternalRequest::False) = 0;
        virtual void handleEmergencyCallEvent(const std::string &number)                         = 0;
        virtual void handleAddContactEvent(const std::string &number)                            = 0;
    };

    class ApplicationCall : public Application, public CallWindowInterface, public EnterNumberWindowInterface
    {
      private:
        sys::TimerHandle callerIdTimer;

      protected:
        call::State callState = call::State::IDLE;

      public:
        explicit ApplicationCall(std::string name                    = name_call,
                                 std::string parent                  = {},


@@ 90,10 90,16 @@ namespace app
        void handleIncomingCall();
        void handleCallerId(const app::manager::actions::CallParams *params);
        void handleEmergencyCallEvent(const std::string &number) override;
        void handleCallEvent(const std::string &number) override;
        void handleCallEvent(const std::string &number, ExternalRequest isExternalRequest) override;
        void handleAddContactEvent(const std::string &number) override;

        auto showNotification(std::function<bool()> action, const std::string &icon, const std::string &text) -> bool;
        enum class NotificationType
        {
            Info,
            Failure
        };
        auto showNotificationAndRestartCallFlow(NotificationType type, const std::string &text) -> bool;

        [[nodiscard]] auto getCallState() const noexcept -> call::State override
        {


@@ 121,6 127,15 @@ namespace app
        void transmitDtmfTone(uint32_t digit) override;
        void hangupCall() override;
        void answerIncomingCall() override;

      private:
        sys::TimerHandle callerIdTimer;

      protected:
        call::State callState           = call::State::IDLE;
        ExternalRequest externalRequest = ExternalRequest::False;

        bool conditionalReturnToPreviousView();
    };

    template <> struct ManifestTraits<ApplicationCall>


@@ 133,6 148,8 @@ namespace app
                     manager::actions::EmergencyDial,
                     manager::actions::NotAnEmergencyNotification,
                     manager::actions::NoSimNotification,
                     manager::actions::NoNetworkConnectionNotification,
                     manager::actions::CallRequestGeneralErrorNotification,
                     manager::actions::CallRejectedByOfflineNotification,
                     manager::actions::PhoneModeChanged,
                     manager::actions::ActivateCall,

M module-apps/application-call/windows/CallWindow.cpp => module-apps/application-call/windows/CallWindow.cpp +0 -1
@@ 440,7 440,6 @@ namespace gui
    {
        switch (callEndType) {
        case CallEndType::None:
            [[fallthrough]];
        case CallEndType::Ended:
            durationLabel->setText(utils::translate(strings::callended));
            break;

M module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.cpp => module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.cpp +1 -1
@@ 279,7 279,7 @@ namespace bsp
        }

        RXfer.resultCode = reason;
        logUARTdebug("[RX reason] %s", c_str(reason));
        logUARTdebug("[RX reason] %s", ::c_str(reason));

        xMessageBufferSendFromISR(uartRxBuffer, (void *)&RXfer, RXfer.getSize(), &xHigherPriorityTaskWoken);


M module-cellular/at/src/ATFactory.cpp => module-cellular/at/src/ATFactory.cpp +1 -1
@@ 65,7 65,7 @@ namespace at
        {AT::GET_SCANMODE, {"AT+QCFG=\"nwscanmode\""}},
        {AT::QGMR, {"AT+QGMR"}},
        {AT::STORE_SETTINGS_ATW, {"AT&W"}},
        {AT::CEER, {"AT+CEER"}},
        {AT::CEER, {"AT+CEER", 1s}},
        {AT::QIGETERROR, {"AT+QIGETERROR"}},
        {AT::VTS, {"AT+VTS=", default_long_timeout}},
        {AT::QLDTMF, {"AT+QLDTMF=1,"}},

M module-cellular/modem/ATCommon.cpp => module-cellular/modem/ATCommon.cpp +6 -0
@@ 65,12 65,18 @@ std::string Channel::formatCommand(const std::string &cmd) const
Result Channel::cmd(const std::string &cmd, std::chrono::milliseconds timeout, size_t rxCount)
{
    Result result;
    if (cmd.empty()) {
        LOG_INFO("Skip empty command");
        result.code = Result::Code::OK;
        return result;
    }
    ATStream atStream(rxCount);

    awaitingResponseFlag.set();

    cmdInit();
    std::string cmdFixed = formatCommand(cmd);
    LOG_DEBUG("start of %s", cmdFixed.c_str());
    cmdSend(cmdFixed);

    auto startTime = std::chrono::steady_clock::now();

M module-services/service-appmgr/include/service-appmgr/Actions.hpp => module-services/service-appmgr/include/service-appmgr/Actions.hpp +2 -0
@@ 32,6 32,8 @@ namespace app::manager
            HandleCallerId,
            NotAnEmergencyNotification,
            NoSimNotification,
            NoNetworkConnectionNotification,
            CallRequestGeneralErrorNotification,
            Dial,
            EmergencyDial,
            ShowCallLog,

M module-services/service-appmgr/include/service-appmgr/data/CallActionsParams.hpp => module-services/service-appmgr/include/service-appmgr/data/CallActionsParams.hpp +29 -0
@@ 19,4 19,33 @@ namespace app::manager::actions
      private:
        utils::PhoneNumber::View number;
    };

    class CallRequestGeneralErrorParams : public app::manager::actions::ActionParams
    {
      public:
        struct Error
        {
            enum class Type
            {
                Error,
                CmeError,
                CmsError,
                ModemTimeout,
                UndefinedError,
                TransmissionError,
                ChannelNotReadyError
            } type;
        };

        explicit CallRequestGeneralErrorParams(Error error) : error{error}
        {}

        [[nodiscard]] auto getError() const noexcept
        {
            return error;
        }

      private:
        Error error;
    };
} // namespace app::manager::actions

M module-services/service-cellular/CellularRequestHandler.cpp => module-services/service-cellular/CellularRequestHandler.cpp +14 -9
@@ 43,6 43,7 @@ void CellularRequestHandler::handle(cellular::ImeiRequest &request, at::Result &

    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, service::name::appmgr);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::UssdRequest &request, at::Result &result)


@@ 56,7 57,7 @@ void CellularRequestHandler::handle(cellular::UssdRequest &request, at::Result &
    else {
        sendMmiResult(requestHandled);
    }
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::CallRequest &request, at::Result &result)


@@ 83,28 84,32 @@ void CellularRequestHandler::handle(cellular::RejectRequest &request, at::Result
        auto message = std::make_shared<CellularNotAnEmergencyNotification>(request.getNumber());
        cellular.bus.sendUnicast(message, service::name::appmgr);
    }
    else if (request.getRejectReason() == cellular::RejectRequest::RejectReason::NoNetworkConnection) {
        auto message = std::make_shared<CellularNoNetworkConenctionNotification>();
        cellular.bus.sendUnicast(message, service::name::appmgr);
    }
    request.setHandled(true);
}

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

void CellularRequestHandler::handle(cellular::PasswordRegistrationRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);
    request.setHandled(requestHandled);
    sendMmiResult(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::PinChangeRequest &request, at::Result &result)
{
    auto requestHandled = request.checkModemResponse(result);
    request.setHandled(requestHandled);
    sendMmiResult(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::ClirRequest &request, at::Result &result)


@@ 136,7 141,7 @@ void CellularRequestHandler::handle(cellular::ClirRequest &request, at::Result &
    }
    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, ::service::name::appmgr);
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::CallForwardingRequest &request, at::Result &result)


@@ 201,7 206,7 @@ void CellularRequestHandler::handle(cellular::CallForwardingRequest &request, at
    }
    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, ::service::name::appmgr);
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::CallBarringRequest &request, at::Result &result)


@@ 250,7 255,7 @@ void CellularRequestHandler::handle(cellular::CallBarringRequest &request, at::R

    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, ::service::name::appmgr);
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::ClipRequest &request, at::Result &result)


@@ 288,7 293,7 @@ void CellularRequestHandler::handle(cellular::ClipRequest &request, at::Result &

    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, ::service::name::appmgr);
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::handle(cellular::CallWaitingRequest &request, at::Result &result)


@@ 335,7 340,7 @@ void CellularRequestHandler::handle(cellular::CallWaitingRequest &request, at::R
    }
    auto msg = std::make_shared<CellularMMIResultMessage>(MMIResultParams::MMIResult::Success, response);
    cellular.bus.sendUnicast(msg, ::service::name::appmgr);
    request.setHandled(requestHandled);
    request.setHandled(true);
}

void CellularRequestHandler::sendMmiResult(bool result)

M module-services/service-cellular/RequestFactory.cpp => module-services/service-cellular/RequestFactory.cpp +17 -1
@@ 16,6 16,7 @@
#include "service-cellular/requests/UssdRequest.hpp"
#include "service-cellular/requests/RejectRequest.hpp"

#include <at/ATFactory.hpp>
#include <EventStore.hpp>
#include <cmd/QECCNUM.hpp>



@@ 67,9 68,21 @@ namespace cellular
        return nullptr;
    }

    bool RequestFactory::isConnectedToNetwork()
    {
        at::Cmd buildCmd = at::factory(at::AT::COPS) + "?";
        auto resp        = channel.cmd(buildCmd);
        at::response::cops::CurrentOperatorInfo ret;
        if ((resp.code == at::Result::Code::OK) && (at::response::parseCOPS(resp, ret))) {
            return ret.getOperator().has_value();
        }
        return false;
    }

    std::unique_ptr<IRequest> RequestFactory::create()
    {
        if (auto req = emergencyCheck(); req) {
        auto isRegisteredToNetwork = isConnectedToNetwork();
        if (auto req = emergencyCheck(); req && isRegisteredToNetwork) {
            return req;
        }



@@ 119,6 132,9 @@ namespace cellular
        if (!simInserted) {
            return std::make_unique<RejectRequest>(RejectRequest::RejectReason::NoSim, request);
        }
        if (!isRegisteredToNetwork) {
            return std::make_unique<RejectRequest>(RejectRequest::RejectReason::NoNetworkConnection, request);
        }
        return std::make_unique<CallRequest>(request);
    }


M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +55 -14
@@ 1745,22 1745,63 @@ auto ServiceCellular::handleCellularAnswerIncomingCallMessage(CellularMessage *m
    return std::make_shared<CellularResponseMessage>(ret);
}

namespace
{
    constexpr auto translate(at::Result::Code code) -> CellularCallRequestGeneralError::ErrorType
    {
        switch (code) {
        case at::Result::Code::ERROR:
        case at::Result::Code::CME_ERROR:
        case at::Result::Code::CMS_ERROR:
            return CellularCallRequestGeneralError::ErrorType::Error;
        case at::Result::Code::TIMEOUT:
            return CellularCallRequestGeneralError::ErrorType::ModemTimeout;
        case at::Result::Code::TOKENS:
        case at::Result::Code::PARSING_ERROR:
        case at::Result::Code::FULL_MSG_BUFFER:
        case at::Result::Code::TRANSMISSION_NOT_STARTED:
        case at::Result::Code::RECEIVING_NOT_STARTED:
        case at::Result::Code::DATA_NOT_USED:
        case at::Result::Code::CMUX_FRAME_ERROR:
            return CellularCallRequestGeneralError::ErrorType::TransmissionError;
        case at::Result::Code::OK:
        case at::Result::Code::NONE:
        case at::Result::Code::UNDEFINED:
            return CellularCallRequestGeneralError::ErrorType::UndefinedError;
        }
        return CellularCallRequestGeneralError::ErrorType::UndefinedError;
    }
} // namespace

auto ServiceCellular::handleCellularCallRequestMessage(CellularCallRequestMessage *msg)
    -> std::shared_ptr<CellularResponseMessage>
{
    LOG_INFO("%s", __PRETTY_FUNCTION__);
    auto channel = cmux->get(CellularMux::Channel::Commands);
    if (channel == nullptr) {
        LOG_WARN("commands channel not ready");
        auto message = std::make_shared<CellularCallRequestGeneralError>(
            CellularCallRequestGeneralError::ErrorType::ChannelNotReadyError);
        bus.sendUnicast(message, ::service::name::appmgr);
        return std::make_shared<CellularResponseMessage>(false);
    }
    cellular::RequestFactory factory(
        msg->number.getEntered(), *channel, msg->callMode, Store::GSM::get()->simCardInserted());

    auto request = factory.create();

    CellularRequestHandler handler(*this);
    auto result = channel->cmd(request->command());
    request->handle(handler, result);
    if (!result) {
        log_last_AT_error(channel);
    }
    LOG_INFO("isHandled %d, %s", static_cast<int>(request->isHandled()), utils::enumToString(result.code).c_str());
    if (!request->isHandled()) {
        CellularCallRequestGeneralError::ErrorType errorType = translate(result.code);
        auto message = std::make_shared<CellularCallRequestGeneralError>(errorType);
        bus.sendUnicast(message, ::service::name::appmgr);
    }

    return std::make_shared<CellularResponseMessage>(request->isHandled());
}



@@ 1961,22 2002,22 @@ auto ServiceCellular::handleCellularGetFirmwareVersionMessage(sys::Message *msg)
auto ServiceCellular::handleEVMStatusMessage(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    using namespace bsp::cellular::status;
    auto message = static_cast<sevm::StatusStateMessage *>(msg);
        auto status_pin = message->state;
        if (priv->modemResetHandler->handleStatusPinEvent(status_pin == value::ACTIVE)) {
            return std::make_shared<CellularResponseMessage>(true);
        }
    auto message    = static_cast<sevm::StatusStateMessage *>(msg);
    auto status_pin = message->state;
    if (priv->modemResetHandler->handleStatusPinEvent(status_pin == value::ACTIVE)) {
        return std::make_shared<CellularResponseMessage>(true);
    }

        if (status_pin == value::ACTIVE) {
            if (priv->state->get() == State::ST::PowerUpProcedure) {
                priv->state->set(State::ST::PowerUpInProgress); // and go to baud detect as usual
            }
    if (status_pin == value::ACTIVE) {
        if (priv->state->get() == State::ST::PowerUpProcedure) {
            priv->state->set(State::ST::PowerUpInProgress); // and go to baud detect as usual
        }
        if (status_pin == value::INACTIVE) {
            if (priv->state->get() == State::ST::PowerDownWaiting) {
                priv->state->set(State::ST::PowerDown);
            }
    }
    if (status_pin == value::INACTIVE) {
        if (priv->state->get() == State::ST::PowerDownWaiting) {
            priv->state->set(State::ST::PowerDown);
        }
    }
    return std::make_shared<CellularResponseMessage>(true);
}


M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +37 -0
@@ 545,6 545,43 @@ class CellularNotAnEmergencyNotification : public CellularResponseMessage,
    }
};

class CellularNoNetworkConenctionNotification : public CellularResponseMessage,
                                                public app::manager::actions::ConvertibleToAction
{
  public:
    CellularNoNetworkConenctionNotification() : CellularResponseMessage(false)
    {}

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

class CellularCallRequestGeneralError : public CellularResponseMessage,
                                        public app::manager::actions::ConvertibleToAction
{
  public:
    using Error     = app::manager::actions::CallRequestGeneralErrorParams::Error;
    using ErrorType = app::manager::actions::CallRequestGeneralErrorParams::Error::Type;

    CellularCallRequestGeneralError(ErrorType errorType) : CellularResponseMessage(false), error{errorType}
    {}

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

  private:
    Error error;
};

class CellularNewIncomingSMSMessage : public CellularMessage
{
  public:

M module-services/service-cellular/service-cellular/RequestFactory.hpp => module-services/service-cellular/service-cellular/RequestFactory.hpp +1 -0
@@ 30,6 30,7 @@ namespace cellular
      private:
        void registerRequest(std::string regex, CreateCallback);
        std::unique_ptr<IRequest> emergencyCheck();
        bool isConnectedToNetwork();

        std::string request;
        std::vector<std::pair<std::string, CreateCallback>> requestMap;

M module-services/service-cellular/service-cellular/requests/RejectRequest.hpp => module-services/service-cellular/service-cellular/requests/RejectRequest.hpp +2 -1
@@ 17,7 17,8 @@ namespace cellular
        enum class RejectReason
        {
            NoSim,
            NotAnEmergencyNumber
            NotAnEmergencyNumber,
            NoNetworkConnection
        };

        RejectRequest(RejectReason rejectReason, const std::string &number)

M module-services/service-cellular/tests/unittest_request_factory.cpp => module-services/service-cellular/tests/unittest_request_factory.cpp +42 -16
@@ 34,6 34,8 @@ TEST_CASE("Emergency handling")
        bool isNumberEmergencyNoSim;
        // mock that sim is inserted
        bool insertSim;
        // mock that phone is connected to network
        bool isConnectedToNetwork;
        // reject reason if applicable
        std::optional<RejectRequest::RejectReason> rejectReason;
        // expected typeid name of created request


@@ 49,42 51,59 @@ TEST_CASE("Emergency handling")
        ////// normal request

        // no SIM and SIM emergency number / sim inserted
        {false, true, true, true, std::nullopt, typeid(CallRequest).name()},
        {false, true, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM and SIM emergency number / sim inserted / no network connection
        {false,
         true,
         true,
         true,
         false,
         RejectRequest::RejectReason::NoNetworkConnection,
         typeid(RejectRequest).name(),
         ""},
        // no SIM emergency number / sim inserted
        {false, false, true, true, std::nullopt, typeid(CallRequest).name()},
        {false, false, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / sim inserted
        {false, true, false, true, std::nullopt, typeid(CallRequest).name()},
        {false, true, false, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM and SIM emergency number / no sim inserted
        {false, true, true, false, std::nullopt, typeid(CallRequest).name()},
        {false, true, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM emergency number / no sim inserted
        {false, false, true, false, std::nullopt, typeid(CallRequest).name()},
        {false, false, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / no sim inserted
        {false, true, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        {false, true, false, false, true, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        // normal number / sim inserted
        {false, false, false, true, std::nullopt, typeid(CallRequest).name()},
        {false, false, false, true, true, std::nullopt, typeid(CallRequest).name()},
        // normal number / no sim inserted
        {false, false, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        {false, false, false, false, true, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},

        ////// emergency request
        // no SIM and SIM emergency number / sim inserted
        {true, true, true, true, std::nullopt, typeid(CallRequest).name()},
        {true, true, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM emergency number / sim inserted
        {true, false, true, true, std::nullopt, typeid(CallRequest).name()},
        {true, false, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / sim inserted
        {true, true, false, true, std::nullopt, typeid(CallRequest).name()},
        {true, true, false, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM and SIM emergency number / no sim inserted
        {true, true, true, false, std::nullopt, typeid(CallRequest).name()},
        {true, true, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM emergency number / no sim inserted
        {true, false, true, false, std::nullopt, typeid(CallRequest).name()},
        {true, false, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / no sim inserted
        {true, true, false, false, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        {true, true, false, false, true, RejectRequest::RejectReason::NoSim, typeid(RejectRequest).name(), ""},
        // normal number / sim inserted
        {true, false, false, true, RejectRequest::RejectReason::NotAnEmergencyNumber, typeid(RejectRequest).name(), ""},
        {true,
         false,
         false,
         true,
         true,
         RejectRequest::RejectReason::NotAnEmergencyNumber,
         typeid(RejectRequest).name(),
         ""},
        // normal number / no sim inserted
        {true,
         false,
         false,
         false,
         true,
         RejectRequest::RejectReason::NotAnEmergencyNumber,
         typeid(RejectRequest).name(),
         ""},


@@ 106,6 125,9 @@ TEST_CASE("Emergency handling")
        if (test.isNumberEmergencySim) {
            channelMockResponse.emplace_back("+QECCNUM: 1,\"" + test.number + "\"");
        }
        if (test.isConnectedToNetwork) {
            channelMockResponse.emplace_back("+COPS: 0,0,\"PLAY\",7");
        }

        auto dummyChannel = at::GenericChannel(at::Result::Code::OK, channelMockResponse);



@@ 443,7 465,11 @@ TEST_CASE("MMI requests")
    };

    for (auto &testCase : testCases) {
        auto mockChannel = at::GenericChannel(at::Result::Code::OK, {});
        std::vector<std::string> channelMockResponse;
        // connected to network
        channelMockResponse.emplace_back("+COPS: 0,0,\"PLAY\",7");

        auto mockChannel = at::GenericChannel(at::Result::Code::OK, channelMockResponse);
        RequestFactory requestFactory(testCase.requestString, mockChannel, cellular::api::CallMode::Regular, true);
        auto request        = requestFactory.create();
        auto requestCommand = request->command();

M products/PurePhone/services/appmgr/ApplicationManager.cpp => products/PurePhone/services/appmgr/ApplicationManager.cpp +2 -0
@@ 358,6 358,8 @@ namespace app::manager
        connect(typeid(CellularMMIPushMessage), convertibleToActionHandler);
        connect(typeid(CellularNoSimNotification), convertibleToActionHandler);
        connect(typeid(CellularNotAnEmergencyNotification), convertibleToActionHandler);
        connect(typeid(CellularNoNetworkConenctionNotification), convertibleToActionHandler);
        connect(typeid(CellularCallRequestGeneralError), convertibleToActionHandler);
        connect(typeid(CellularSmsNoSimRequestMessage), convertibleToActionHandler);
        connect(typeid(CellularSMSRejectedByOfflineNotification), convertibleToActionHandler);
        connect(typeid(CellularCallRejectedByOfflineNotification), convertibleToActionHandler);