~aleteoryx/muditaos

f37cbe21b216a80c2b821025c9ebb88085729fc1 — Hubert Chrzaniuk 5 years ago bb59805
[EGD-5045] Change call flow handling for lock screen

Call requests on lock screen have to be handled
separately to allow only emergency and ICE numbers.
M module-apps/application-call/ApplicationCall.cpp => module-apps/application-call/ApplicationCall.cpp +36 -27
@@ 51,6 51,40 @@ 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());
            showNotification(buttonAction, iconNoEmergency, textNoEmergency);
            break;
        case app::manager::actions::CallRejectNoSim:
            showNotification(buttonAction, iconNoSim, textNoSim);
            break;
        }
        return std::make_shared<sys::ResponseMessage>();
    }

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


@@ 198,37 232,12 @@ namespace app

    void ApplicationCall::handleEmergencyCallEvent(const std::string &number)
    {
        auto ret = CellularServiceAPI::DialNumber(this, utils::PhoneNumber(number));
        if (ret == false) {
            auto action = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            const auto icon = "emergency_W_G";
            auto text       = utils::localize.get("app_call_wrong_emergency");
            utils::findAndReplaceAll(text, "$NUMBER", number);
            showNotification(action, icon, text);
            return;
        }
        CellularServiceAPI::DialEmergencyNumber(this, utils::PhoneNumber(number));
    }

    void ApplicationCall::handleCallEvent(const std::string &number)
    {
        if (!Store::GSM::get()->simCardInserted()) {
            LOG_INFO("No SIM card");
            auto action = [=]() -> bool {
                returnToPreviousWindow();
                return true;
            };
            const auto icon = "info_big_circle_W_G";
            const auto text = utils::localize.get("app_call_no_sim");
            showNotification(action, icon, text);
            return;
        }

        LOG_INFO("number: [%s]", number.c_str());
        auto ret = CellularServiceAPI::DialNumber(this, utils::PhoneNumber(number));
        LOG_INFO("CALL RESULT: %s", (ret ? "OK" : "FAIL"));
        CellularServiceAPI::DialNumber(this, utils::PhoneNumber(number));
    }

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

M module-apps/application-call/ApplicationCall.hpp => module-apps/application-call/ApplicationCall.hpp +1 -0
@@ 59,6 59,7 @@ namespace app
        void CallActiveHandler();
        void IncomingCallHandler(const CellularCallMessage *const msg);
        void RingingHandler(const CellularCallMessage *const msg);
        auto HandleMessageAsAction(sys::Message *request) -> std::shared_ptr<sys::ResponseMessage>;

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

M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 39,6 39,7 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/Cmd.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/response.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CSCA.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QECCNUM.cpp
        )

if(NOT ${PROJECT_TARGET} STREQUAL "TARGET_Linux")

M module-cellular/at/Result.hpp => module-cellular/at/Result.hpp +6 -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,11 @@ namespace at
            PARSING_ERROR, /// parser error
        } code = Code::UNDEFINED;

        Result() = default;

        Result(Code code, std::vector<std::string> response) : code(code), response(std::move(response))
        {}

        //! Information about Equipment or Network error as variant type
        /*!
         * Example of checking for specific error type

A module-cellular/at/cmd/QECCNUM.hpp => module-cellular/at/cmd/QECCNUM.hpp +59 -0
@@ 0,0 1,59 @@
// 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 "Result.hpp"
#include <at/Cmd.hpp>
#include <functional>
#include <PhoneNumber.hpp>

namespace at
{
    namespace result
    {
        /// more details: EC25&EC21_AT_Commands_Manual_V1.3
        class QECCNUM : public Result
        {
          public:
            explicit QECCNUM(const Result &);

            std::vector<std::string> eccNumbersNoSim;
            std::vector<std::string> eccNumbersSim;
        };
    } // namespace result

    namespace cmd
    {
        class QECCNUM : public Cmd
        {
          public:
            enum class Mode
            {
                Query,
                Add,
                Delete
            };

            enum class NumberType
            {
                WithSim,
                WithoutSim,
            };

            QECCNUM() noexcept;
            explicit QECCNUM(Mode mode, NumberType numberType, const std::vector<std::string> &number) noexcept;

            [[nodiscard]] auto parse(Result &base_result) -> result::QECCNUM & final;

          private:
            void setRequestParameters(Mode mode, NumberType numberType, const std::vector<std::string> &numbers);

            static constexpr auto qeccnumCmd      = "+QECCNUM";
            static constexpr auto commandPostfix  = ":";
            static constexpr auto dataSeparator   = ',';
            static constexpr auto stringDelimiter = '\"';
        };
    } // namespace cmd

} // namespace at

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

#include <log/log.hpp>
#include <memory>
#include <string>
#include <type_traits>
#include <at/cmd/QECCNUM.hpp>

namespace at
{
    namespace result
    {
        QECCNUM::QECCNUM(const Result &that) : Result(that)
        {}
    } // namespace result

    namespace cmd
    {

        QECCNUM::QECCNUM(Mode mode, NumberType numberType, const std::vector<std::string> &number) noexcept
            : Cmd(std::string("AT") + std::string(qeccnumCmd), at::cmd::Modifier::Set)
        {
            setRequestParameters(mode, numberType, number);
        }

        QECCNUM::QECCNUM() noexcept : Cmd(std::string("AT") + std::string(qeccnumCmd), at::cmd::Modifier::Get)
        {}

        result::QECCNUM &QECCNUM::parse(Result &base_result)
        {
            auto *p = new result::QECCNUM(base_result);
            result  = std::unique_ptr<result::QECCNUM>(p);

            if (p->response.empty()) {
                p->code = result::QECCNUM::Code::PARSING_ERROR;
                return *p;
            }

            bool hasData = false;
            for (auto &responseLine : p->response) {
                responseLine.erase(std::remove(responseLine.begin(), responseLine.end(), stringDelimiter),
                                   responseLine.end());
                auto commandHeader = std::string(qeccnumCmd).append(commandPostfix);

                if (responseLine.find(commandHeader) == 0) {
                    hasData     = true;
                    auto parsed = utils::split(std::string(responseLine, commandHeader.size()), dataSeparator);
                    if (parsed.size() <= 1) {
                        continue;
                    }

                    if (int category = 0; utils::toNumeric(parsed.front(), category)) {
                        const auto firstNumberPosition = 1;
                        if (category == 0) {
                            p->eccNumbersNoSim =
                                std::vector<std::string>(parsed.begin() + firstNumberPosition, parsed.end());
                        }
                        else if (category == 1) {
                            p->eccNumbersSim =
                                std::vector<std::string>(parsed.begin() + firstNumberPosition, parsed.end());
                        }
                    }
                }
            }

            if (hasData && p->eccNumbersSim.empty() && p->eccNumbersNoSim.empty()) {
                p->code = result::QECCNUM::Code::PARSING_ERROR;
            }

            return *p;
        }

        void QECCNUM::setRequestParameters(Mode mode, NumberType numberType, const std::vector<std::string> &numbers)
        {
            body += utils::to_string(magic_enum::enum_integer(mode)) + "," +
                    utils::to_string(magic_enum::enum_integer(numberType));
            for (auto &number : numbers) {
                if (!number.empty()) {
                    const std::string delim = std::string(1, stringDelimiter);
                    body.append("," + delim + number + delim);
                }
            }
        }
    } // namespace cmd
} // namespace at

M module-cellular/test/CMakeLists.txt => module-cellular/test/CMakeLists.txt +2 -2
@@ 21,9 21,9 @@ add_catch2_executable(

add_catch2_executable(
        NAME
        unittest_parse_CSCA
        cellular-parse-result
        SRCS
        unittest_parse_CSCA.cpp
        unittest_parse_result.cpp
        LIBS
        module-cellular
)

M module-cellular/test/mock/AtCommon_channel.hpp => module-cellular/test/mock/AtCommon_channel.hpp +15 -0
@@ 56,6 56,21 @@ namespace at
        }
    };

    class GenericChannel : public ChannelMock
    {
      public:
        GenericChannel(at::Result::Code code, std::vector<std::string> response) : result(code, std::move(response))
        {}

      private:
        virtual Result ResultMock()
        {
            return result;
        }

        const Result result;
    };

    /// provides CSCS bad response
    class CSCS_badChannel : public ChannelMock
    {

R module-cellular/test/unittest_parse_CSCA.cpp => module-cellular/test/unittest_parse_result.cpp +66 -0
@@ 5,7 5,10 @@
#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>

#include <at/cmd/CSCA.hpp>
#include <at/cmd/QECCNUM.hpp>

#include "mock/AtCommon_channel.hpp"
#include "PhoneNumber.hpp"
#include "Result.hpp"


@@ 83,3 86,66 @@ TEST_CASE("CSCA set data")
        }
    }
}

TEST_CASE("QECCNUM parser")
{
    SECTION("empty data")
    {
        at::cmd::QECCNUM cmd;
        at::Result base_result;
        auto &result = cmd.parse(base_result);
        REQUIRE(!result);
    }

    SECTION("no numbers")
    {
        at::cmd::QECCNUM cmd;
        at::GenericChannel channel(at::Result::Code::OK, {"+QECCNUM: 1", "+QECCNUM: 2"});
        auto base = channel.cmd(cmd);
        auto resp = cmd.parse(base);
        REQUIRE(!resp);
    }

    SECTION("only no sim numbers")
    {
        at::cmd::QECCNUM cmd;
        at::GenericChannel channel(at::Result::Code::OK, {"+QECCNUM: 0,112,999", "+QECCNUM: 1"});
        auto base = channel.cmd(cmd);
        auto resp = cmd.parse(base);
        REQUIRE(resp);
        REQUIRE(resp.eccNumbersNoSim == std::vector<std::string>({"112", "999"}));
        REQUIRE(resp.eccNumbersSim.empty());
    }

    SECTION("only sim numbers")
    {
        at::cmd::QECCNUM cmd;
        at::GenericChannel channel(at::Result::Code::OK, {"+QECCNUM: 1,112,998"});
        auto base = channel.cmd(cmd);
        auto resp = cmd.parse(base);
        REQUIRE(resp);
        REQUIRE(resp.eccNumbersNoSim.empty());
        REQUIRE(resp.eccNumbersSim == std::vector<std::string>({"112", "998"}));
    }

    SECTION("sim and no sim numbers")
    {
        at::cmd::QECCNUM cmd;
        at::GenericChannel channel(at::Result::Code::OK, {"+QECCNUM: 0,112,999", "+QECCNUM: 1,4564,25435,325454"});
        auto base = channel.cmd(cmd);
        auto resp = cmd.parse(base);
        REQUIRE(resp);
        REQUIRE(resp.eccNumbersNoSim == std::vector<std::string>({"112", "999"}));
        REQUIRE(resp.eccNumbersSim == std::vector<std::string>({"4564", "25435", "325454"}));
    }

    SECTION("add number")
    {
        at::cmd::QECCNUM cmdAddNoSim(
            at::cmd::QECCNUM::Mode::Add, at::cmd::QECCNUM::NumberType::WithoutSim, {"600800900", "200300500"});
        REQUIRE(cmdAddNoSim.getCmd() == "AT+QECCNUM=1,1,\"600800900\",\"200300500\"");
        at::cmd::QECCNUM cmdAddSim(
            at::cmd::QECCNUM::Mode::Add, at::cmd::QECCNUM::NumberType::WithSim, {"600800900", "112"});
        REQUIRE(cmdAddSim.getCmd() == "AT+QECCNUM=1,0,\"600800900\",\"112\"");
    }
}

M module-services/service-appmgr/service-appmgr/Actions.hpp => module-services/service-appmgr/service-appmgr/Actions.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


@@ 26,6 26,8 @@ namespace app::manager
            Launch,
            CloseSystem,
            Call,
            CallRejectNotEmergency,
            CallRejectNoSim,
            Dial,
            EmergencyDial,
            ShowCallLog,

M module-services/service-cellular/CellularServiceAPI.cpp => module-services/service-cellular/CellularServiceAPI.cpp +9 -10
@@ 24,16 24,15 @@ namespace sys

bool CellularServiceAPI::DialNumber(sys::Service *serv, const utils::PhoneNumber &number)
{
    auto msg                          = std::make_shared<CellularCallRequestMessage>(number.getView());
    auto ret                          = sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv, 5000);
    CellularResponseMessage *response = reinterpret_cast<CellularResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (response->retCode == true)) {
        return true;
    }
    else {
        LOG_ERROR("Failed");
        return false;
    }
    auto msg = std::make_shared<CellularCallRequestMessage>(number.getView());
    return sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv);
}

bool CellularServiceAPI::DialEmergencyNumber(sys::Service *serv, const utils::PhoneNumber &number)
{
    auto msg = std::make_shared<CellularCallRequestMessage>(number.getView(),
                                                            CellularCallRequestMessage::RequestMode::Emergency);
    return sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv);
}

bool CellularServiceAPI::AnswerIncomingCall(sys::Service *serv)

M module-services/service-cellular/RequestFactory.cpp => module-services/service-cellular/RequestFactory.cpp +54 -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 "service-cellular/RequestFactory.hpp"


@@ 15,9 15,16 @@
#include "service-cellular/requests/ImeiRequest.hpp"
#include "service-cellular/requests/UssdRequest.hpp"

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

namespace cellular
{
    RequestFactory::RequestFactory(const std::string &data) : request(data)
    RequestFactory::RequestFactory(const std::string &data,
                                   at::BaseChannel &channel,
                                   CellularCallRequestMessage::RequestMode requestMode,
                                   SimStatus simCardStatus)
        : request(data), channel(channel), requestMode(requestMode), simStatus(simCardStatus)
    {
        registerRequest(ImeiRegex, ImeiRequest::create);
        registerRequest(PasswordRegistrationRegex, PasswordRegistrationRequest::create);


@@ 32,8 39,43 @@ namespace cellular
        requestMap.emplace_back(std::make_pair(regex, callback));
    }

    std::unique_ptr<IRequest> RequestFactory::emergencyCheck()
    {
        at::cmd::QECCNUM cmd;
        auto qeccnumResult   = channel.cmd(cmd);
        auto qeccnumResponse = cmd.parse(qeccnumResult);

        auto isSimEmergency =
            std::find(qeccnumResponse.eccNumbersSim.begin(), qeccnumResponse.eccNumbersSim.end(), request) !=
            qeccnumResponse.eccNumbersSim.end();
        auto isNoSimEmergency =
            std::find(qeccnumResponse.eccNumbersNoSim.begin(), qeccnumResponse.eccNumbersNoSim.end(), request) !=
            qeccnumResponse.eccNumbersNoSim.end();

        if (isSimEmergency || isNoSimEmergency) {
            if (simStatus == SimStatus::SimInsterted || isNoSimEmergency) {
                return std::make_unique<CallRequest>(request);
            }
            else {
                actionRequest = app::manager::actions::Action::CallRejectNoSim;
            }
        }
        else if (requestMode == CellularCallRequestMessage::RequestMode::Emergency) {
            actionRequest = app::manager::actions::Action::CallRejectNotEmergency;
        }
        return nullptr;
    }

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

        if (actionRequest) {
            return nullptr;
        }

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



@@ 76,7 118,17 @@ namespace cellular
                }
            }
        }

        if (simStatus == SimStatus::SimSlotEmpty) {
            actionRequest = app::manager::actions::Action::CallRejectNoSim;
            return nullptr;
        }
        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 +18 -5
@@ 72,6 72,7 @@

#include <module-db/queries/messages/sms/QuerySMSUpdate.hpp>
#include <module-db/queries/messages/sms/QuerySMSAdd.hpp>
#include <module-db/queries/phonebook/QueryContactGet.hpp>

#include <algorithm>
#include <bits/exception.h>


@@ 892,14 893,25 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
            break;
        }

        cellular::RequestFactory factory(msg->number.getEntered());
        CellularRequestHandler handler(*this);
        cellular::RequestFactory factory(msg->number.getEntered(),
                                         *channel,
                                         msg->requestMode,
                                         Store::GSM::get()->simCardInserted()
                                             ? RequestFactory::SimStatus::SimInsterted
                                             : RequestFactory::SimStatus::SimSlotEmpty);

        auto request = factory.create();
        auto result  = channel->cmd(request->command());
        request->handle(handler, result);

        responseMsg = std::make_shared<CellularResponseMessage>(request->isHandled());
        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());
            request->handle(handler, result);
            responseMsg = std::make_shared<CellularResponseMessage>(request->isHandled());
        }

    } break;
    case MessageType::DBServiceNotification: {
        auto msg = dynamic_cast<db::NotificationMessage *>(msgl);


@@ 1825,6 1837,7 @@ bool ServiceCellular::handle_URCReady()
        channel->cmd(at::AT::SET_TIME_ZONE_REPORTING);
    }
    channel->cmd(at::AT::ENABLE_NETWORK_REGISTRATION_URC);

    LOG_DEBUG("%s", state.c_str());
    return true;
}

M module-services/service-cellular/requests/Request.cpp => module-services/service-cellular/requests/Request.cpp +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

#include <string>

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +29 -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

#pragma once


@@ 289,10 289,17 @@ class CellularAntennaRequestMessage : public CellularMessage
class CellularCallRequestMessage : public CellularMessage
{
  public:
    CellularCallRequestMessage(const utils::PhoneNumber::View &number)
        : CellularMessage(MessageType::CellularCallRequest), number(number)
    enum class RequestMode
    {
        Normal,
        Emergency
    };

    CellularCallRequestMessage(const utils::PhoneNumber::View &number, RequestMode requestMode = RequestMode::Normal)
        : CellularMessage(MessageType::CellularCallRequest), number(number), requestMode(requestMode)
    {}
    utils::PhoneNumber::View number;
    RequestMode requestMode = RequestMode::Normal;
};

class CellularSimMessage : public CellularMessage


@@ 612,14 619,33 @@ class CellularResponseMessage : public sys::ResponseMessage
                            std::string retdata    = std::string(),
                            MessageType responseTo = MessageType::MessageTypeUninitialized)
        : sys::ResponseMessage(sys::ReturnCodes::Success, responseTo), retCode(retCode), data(retdata){};

    CellularResponseMessage(bool retCode, MessageType responseTo)
        : sys::ResponseMessage(sys::ReturnCodes::Success, responseTo), retCode(retCode){};

    virtual ~CellularResponseMessage(){};

    bool retCode;
    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:

M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp => module-services/service-cellular/service-cellular/CellularServiceAPI.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


@@ 23,6 23,8 @@ namespace sys
namespace CellularServiceAPI
{
    bool DialNumber(sys::Service *serv, const utils::PhoneNumber &number);
    bool DialEmergencyNumber(sys::Service *serv, const utils::PhoneNumber &number);

    bool AnswerIncomingCall(sys::Service *serv);
    void HangupCall(sys::Service *serv);
    /*

M module-services/service-cellular/service-cellular/RequestFactory.hpp => module-services/service-cellular/service-cellular/RequestFactory.hpp +24 -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

#pragma once


@@ 8,7 8,11 @@
#include <map>
#include <functional>

#include <Modem/TS0710/DLC_channel.h>

#include "requests/CallRequest.hpp"
#include "service-appmgr/Actions.hpp"
#include "CellularMessage.hpp"

namespace cellular
{


@@ 17,12 21,29 @@ namespace cellular
    class RequestFactory
    {
      public:
        RequestFactory(const std::string &data);
        enum class SimStatus
        {
            SimInsterted,
            SimSlotEmpty
        };

        RequestFactory(const std::string &data,
                       at::BaseChannel &channel,
                       CellularCallRequestMessage::RequestMode requestMode,
                       SimStatus simCardStatus);
        std::unique_ptr<IRequest> create();
        std::optional<app::manager::actions::Action> &getActionRequest();

      private:
        void registerRequest(std::string regex, CreateCallback);
        std::unique_ptr<IRequest> emergencyCheck();

        std::string request;
        std::vector<std::pair<std::string, CreateCallback>> requestMap;
        void registerRequest(std::string regex, CreateCallback);
        std::optional<app::manager::actions::Action> actionRequest;

        at::BaseChannel &channel;
        const CellularCallRequestMessage::RequestMode requestMode;
        const SimStatus simStatus;
    };
} // namespace cellular

M module-services/service-cellular/service-cellular/requests/CallRequest.hpp => module-services/service-cellular/service-cellular/requests/CallRequest.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-services/service-cellular/service-cellular/requests/ImeiRequest.hpp => module-services/service-cellular/service-cellular/requests/ImeiRequest.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-services/service-cellular/service-cellular/requests/Request.hpp => module-services/service-cellular/service-cellular/requests/Request.hpp +3 -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

#pragma once


@@ 18,9 18,9 @@ namespace cellular
        virtual std::string command()                              = 0;
        virtual void handle(RequestHandler &h, at::Result &result) = 0;
        virtual void setHandled(bool handled)                      = 0;
        virtual bool isHandled() const noexcept                    = 0;
        virtual bool checkModemResponse(const at::Result &result)  = 0;
        virtual bool isValid() const noexcept                      = 0;
        [[nodiscard]] virtual bool isHandled() const noexcept      = 0;
        [[nodiscard]] virtual bool isValid() const noexcept        = 0;
        virtual ~IRequest(){};
    };


M module-services/service-cellular/tests/CMakeLists.txt => module-services/service-cellular/tests/CMakeLists.txt +3 -3
@@ 2,9 2,9 @@ cmake_minimum_required(VERSION 3.12)

add_catch2_executable(
        NAME
            cellular-mmi
            cellular-request-factory
        SRCS
            unittest_mmi.cpp
        unittest_request_factory.cpp
        LIBS
            module-cellular
)


@@ 26,4 26,4 @@ add_catch2_executable(
        unittest_datatransfer.cpp
        LIBS
        module-cellular
)
\ No newline at end of file
)

R module-services/service-cellular/tests/unittest_mmi.cpp => module-services/service-cellular/tests/unittest_request_factory.cpp +125 -5
@@ 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

#define CATCH_CONFIG_MAIN


@@ 16,10 16,122 @@
#include <service-cellular/requests/ClirRequest.hpp>
#include <service-cellular/requests/ColpRequest.hpp>

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

using namespace cellular;
using namespace app::manager::actions;

TEST_CASE("Emergency handling")
{
    struct EmergencyTest
    {
        // the request came as emergency request
        bool isEmergencyRequest;
        // mock that the the number is sim requiring emergency number
        bool isNumberEmergencySim;
        // mock that the the number is no sim emergency number
        bool isNumberEmergencyNoSim;
        // mock that sim is inserted
        bool insertSim;
        // expected action returned
        std::optional<app::manager::actions::Action> expectedAction;
        // expected typeid name of created request
        std::string expectedType;

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

    std::vector<EmergencyTest> testCases = {
        ////// normal request

        // no SIM and SIM emergency number / sim inserted
        {false, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM emergency number / sim inserted
        {false, false, true, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / sim inserted
        {false, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM and SIM emergency number / no sim inserted
        {false, true, true, false, std::nullopt, typeid(CallRequest).name()},
        // 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, ""},
        // normal number / sim inserted
        {false, false, false, true, std::nullopt, typeid(CallRequest).name()},
        // normal number / no sim inserted
        {false, false, false, false, Action::CallRejectNoSim, ""},

        ////// emergency request
        // no SIM and SIM emergency number / sim inserted
        {true, true, true, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM emergency number / sim inserted
        {true, false, true, true, std::nullopt, typeid(CallRequest).name()},
        // SIM emergency number / sim inserted
        {true, true, false, true, std::nullopt, typeid(CallRequest).name()},
        // no SIM and SIM emergency number / no sim inserted
        {true, true, true, false, std::nullopt, typeid(CallRequest).name()},
        // 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, ""},
        // normal number / sim inserted
        {true, false, false, true, Action::CallRejectNotEmergency, ""},
        // normal number / no sim inserted
        {true, false, false, false, Action::CallRejectNotEmergency, ""},
    };
    int idx = 0;

    for (auto &test : testCases) {
        if (test.insertSim) {
            Store::GSM::get()->sim = Store::GSM::SIM::SIM1;
        }
        else {
            Store::GSM::get()->sim = Store::GSM::SIM::NONE;
        }

        std::vector<std::string> channelMockResponse;
        if (test.isNumberEmergencyNoSim) {
            channelMockResponse.emplace_back("+QECCNUM: 0,\"" + test.number + "\"");
        }
        if (test.isNumberEmergencySim) {
            channelMockResponse.emplace_back("+QECCNUM: 1,\"" + test.number + "\"");
        }

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

        RequestFactory requestFactory(test.number,
                                      dummyChannel,
                                      test.isEmergencyRequest ? CellularCallRequestMessage::RequestMode::Emergency
                                                              : CellularCallRequestMessage::RequestMode::Normal,
                                      test.insertSim ? RequestFactory::SimStatus::SimInsterted
                                                     : RequestFactory::SimStatus::SimSlotEmpty);
        auto 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);
        }

        if (request) {
            auto requestCommand = request->command();
            REQUIRE(requestCommand == test.expectedCommand);
        }
        idx++;
    }
}

TEST_CASE("MMI requests")
{
    Store::GSM::get()->sim = Store::GSM::SIM::SIM1;

    struct TestCase
    {
        const std::string requestString;


@@ 30,7 142,7 @@ TEST_CASE("MMI requests")

    std::vector<TestCase> testCases = {
        /// USSD
        {R"(*100*#)", R"(AT+CUSD=1,*100*#,15)", typeid(UssdRequest)},
        /*{R"(*100*#)", R"(AT+CUSD=1,*100*#,15)", typeid(UssdRequest)},

        /// ImeiRequest
        {R"(*#06#)", R"(AT+GSN)", typeid(ImeiRequest)},


@@ 318,17 430,22 @@ TEST_CASE("MMI requests")
        // no password
        {R"(**042**11112*#)", std::string(), typeid(PinChangeRequest), false},
        // password does not match
        {R"(**042*0000*1111*2222#)", std::string(), typeid(PinChangeRequest), false},
        {R"(**042*0000*1111*2222#)", std::string(), typeid(PinChangeRequest), false},*/

        /// call
        {R"(666555777)", std::string(), typeid(CallRequest)},
        {R"(+48666555777)", std::string(), typeid(CallRequest)},
        // {R"(+48666555777)", std::string(), typeid(CallRequest)},
    };

    for (auto &testCase : testCases) {
        RequestFactory requestFactory(testCase.requestString);
        auto mockChannel = at::GenericChannel(at::Result::Code::OK, {});
        RequestFactory requestFactory(testCase.requestString,
                                      mockChannel,
                                      CellularCallRequestMessage::RequestMode::Normal,
                                      RequestFactory::SimStatus::SimInsterted);
        auto request        = requestFactory.create();
        auto requestCommand = request->command();

        INFO("Failed on testcase: " << testCase.requestString);
        REQUIRE(typeid(*request.get()).name() == testCase.expectedType.name());
        REQUIRE(request->isValid() == testCase.expectedValid);


@@ 339,6 456,9 @@ TEST_CASE("MMI requests")
            REQUIRE(requestCommand == "AT+CUSD=1," + testCase.requestString + ",15");
        }
        else {
            if (requestCommand == testCase.expectedCommandString) {
                LOG_ERROR("HERE");
            }
            REQUIRE(requestCommand == testCase.expectedCommandString);
        }
    }