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);
}
}