M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +7 -1
@@ 15,7 15,13 @@ set(SOURCES
src/URCCounter.cpp
src/VolteHandlerImpl.cpp
src/CSQHandler.cpp
- DTMFCode.cpp
+
+ src/volte/VolteCapabilityHandler.cpp
+ src/volte/ImsiParserUS.cpp
+ src/volte/VolteAllowedUSList.cpp
+ src/volte/VolteCapabilityHandlerCellular.cpp
+
+ DTMFCode.cpp
CellularServiceAPI.cpp
CellularUrcHandler.cpp
M module-services/service-cellular/src/ServiceCellularPriv.cpp => module-services/service-cellular/src/ServiceCellularPriv.cpp +12 -3
@@ 8,6 8,10 @@
#include <service-cellular-api>
#include <service-cellular/Constans.hpp>
+#include <volte/ImsiParserUS.hpp>
+#include <volte/VolteAllowedUSList.hpp>
+#include <volte/VolteCapabilityHandlerCellular.hpp>
+
#include <service-evtmgr/EVMessages.hpp>
#include <service-evtmgr/Constants.hpp>
@@ 38,9 42,14 @@ namespace cellular::internal
imeiGetHandler{std::make_unique<service::ImeiGetHandler>()},
tetheringHandler{std::make_unique<TetheringHandler>()},
volteHandler{std::make_unique<VolteHandler<DLCChannel, ModemResponseParserImpl>>()},
- modemResetHandler{std::make_unique<ModemResetHandler>()}, csqHandler{
- std::make_unique<CSQHandler>(),
- }
+ modemResetHandler{std::make_unique<ModemResetHandler>()},
+ csqHandler{
+ std::make_unique<CSQHandler>(),
+ },
+ volteCapability{
+ std::make_unique<VolteCapabilityHandler>(std::make_unique<cellular::service::ImsiParserUS>(),
+ std::make_unique<cellular::service::VolteAllowedUSList>(),
+ std::make_unique<cellular::service::VolteCapabilityCellular>())}
{
initSimCard();
initSMSSendHandler();
M module-services/service-cellular/src/ServiceCellularPriv.hpp => module-services/service-cellular/src/ServiceCellularPriv.hpp +3 -0
@@ 16,6 16,7 @@
#include "VolteHandlerImpl.hpp"
#include "ModemResetHandler.hpp"
#include "CSQHandler.hpp"
+#include "volte/VolteCapabilityHandler.hpp"
namespace cellular::internal
{
@@ 26,6 27,7 @@ namespace cellular::internal
using service::SimContacts;
using service::State;
using service::TetheringHandler;
+ using service::VolteCapabilityHandler;
using service::VolteHandler;
class ServiceCellularPriv
@@ 42,6 44,7 @@ namespace cellular::internal
std::unique_ptr<VolteHandler<DLCChannel, ModemResponseParserImpl>> volteHandler;
std::unique_ptr<ModemResetHandler> modemResetHandler;
std::unique_ptr<CSQHandler> csqHandler;
+ std::unique_ptr<VolteCapabilityHandler> volteCapability;
State::PowerState nextPowerState = State::PowerState::Off;
std::uint8_t multiPartSMSUID = 0;
A module-services/service-cellular/src/volte/ImsiParserInterface.hpp => module-services/service-cellular/src/volte/ImsiParserInterface.hpp +19 -0
@@ 0,0 1,19 @@
+// 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 "OperatorInfo.hpp"
+#include <string>
+#include <optional>
+
+namespace cellular::service
+{
+ class ImsiParserInteface
+ {
+ public:
+ virtual ~ImsiParserInteface() = default;
+
+ virtual auto parse(const std::string &imsi) -> std::optional<OperatorInfo> = 0;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/ImsiParserUS.cpp => module-services/service-cellular/src/volte/ImsiParserUS.cpp +37 -0
@@ 0,0 1,37 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "ImsiParserUS.hpp"
+#include <log/log.hpp>
+#include <array>
+#include <algorithm>
+#include <stdexcept>
+
+namespace cellular::service
+{
+ constexpr auto usMccCount = 7;
+ const std::array<std::string, usMccCount> usMcc{"310", "311", "312", "313", "314", "315", "316"};
+
+ auto ImsiParserUS::parse(const std::string &imsi) -> std::optional<OperatorInfo>
+ {
+ constexpr auto mccSize = 3;
+ constexpr auto mncSize = 3;
+
+ std::string mcc, mnc;
+ try {
+ mcc = imsi.substr(0, mccSize);
+ mnc = imsi.substr(mccSize, mncSize);
+ }
+ catch (const std::out_of_range &e) {
+ LOG_ERROR("IMSI parsing error: %s", e.what());
+ return std::nullopt;
+ }
+
+ if (std::find(std::begin(usMcc), std::end(usMcc), mcc) == std::end(usMcc)) {
+ LOG_ERROR("Not US MCC.");
+ return std::nullopt;
+ }
+
+ return OperatorInfo(mcc, mnc);
+ }
+} // namespace cellular::service
A module-services/service-cellular/src/volte/ImsiParserUS.hpp => module-services/service-cellular/src/volte/ImsiParserUS.hpp +15 -0
@@ 0,0 1,15 @@
+// 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 "ImsiParserInterface.hpp"
+
+namespace cellular::service
+{
+ class ImsiParserUS : public ImsiParserInteface
+ {
+ public:
+ auto parse(const std::string &imsi) -> std::optional<OperatorInfo> final;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/OperatorInfo.hpp => module-services/service-cellular/src/volte/OperatorInfo.hpp +22 -0
@@ 0,0 1,22 @@
+// 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 <string>
+
+namespace cellular::service
+{
+ struct OperatorInfo
+ {
+ OperatorInfo(const std::string &mcc, const std::string &mnc) : MCC(mcc), MNC(mnc)
+ {}
+ std::string MCC;
+ std::string MNC;
+
+ friend bool operator==(const OperatorInfo &lhs, const OperatorInfo &rhs)
+ {
+ return lhs.MCC == rhs.MCC && lhs.MNC == rhs.MNC;
+ }
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteAllowedListInterface.hpp => module-services/service-cellular/src/volte/VolteAllowedListInterface.hpp +18 -0
@@ 0,0 1,18 @@
+// 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 "OperatorInfo.hpp"
+#include <string>
+
+namespace cellular::service
+{
+ class VolteAllowedListInterface
+ {
+ public:
+ virtual ~VolteAllowedListInterface() = default;
+
+ auto virtual isVolteAllowed(const OperatorInfo &operatorInfo) -> bool = 0;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteAllowedUSList.cpp => module-services/service-cellular/src/volte/VolteAllowedUSList.cpp +37 -0
@@ 0,0 1,37 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "VolteAllowedUSList.hpp"
+#include <log/log.hpp>
+#include <array>
+#include <algorithm>
+
+namespace cellular::service
+{
+
+ constexpr auto allowedOperatorsCount = 12;
+ const std::array<OperatorInfo, allowedOperatorsCount> allowedOperators{OperatorInfo{"310", "053"},
+ OperatorInfo{"310", "120"},
+ OperatorInfo{"310", "260"},
+ OperatorInfo{"310", "530"},
+ OperatorInfo{"310", "770"},
+ OperatorInfo{"311", "490"},
+ OperatorInfo{"311", "660"},
+ OperatorInfo{"311", "880"},
+ OperatorInfo{"311", "882"},
+ OperatorInfo{"312", "190"},
+ OperatorInfo{"312", "250"},
+ OperatorInfo{"312", "530"}};
+
+ auto VolteAllowedUSList::isVolteAllowed(const OperatorInfo &operatorInfo) -> bool
+ {
+ LOG_INFO("Trying to find MCC: %s, MNC: %s", operatorInfo.MCC.c_str(), operatorInfo.MNC.c_str());
+
+ if (std::find(std::begin(allowedOperators), std::end(allowedOperators), operatorInfo) ==
+ std::end(allowedOperators)) {
+ LOG_ERROR("Unable to find. VoLTE not allowed.");
+ return false;
+ }
+ return true;
+ }
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteAllowedUSList.hpp => module-services/service-cellular/src/volte/VolteAllowedUSList.hpp +16 -0
@@ 0,0 1,16 @@
+// 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 "VolteAllowedListInterface.hpp"
+#include "OperatorInfo.hpp"
+
+namespace cellular::service
+{
+ class VolteAllowedUSList : public VolteAllowedListInterface
+ {
+ public:
+ auto isVolteAllowed(const OperatorInfo &operatorInfo) -> bool final;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteCapabilityHandler.cpp => module-services/service-cellular/src/volte/VolteCapabilityHandler.cpp +43 -0
@@ 0,0 1,43 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "VolteCapabilityHandler.hpp"
+#include <modem/mux/CellularMux.h>
+#include <log/log.hpp>
+namespace cellular::service
+{
+ VolteCapabilityHandler::VolteCapabilityHandler(std::unique_ptr<ImsiParserInteface> imsiParser,
+ std::unique_ptr<VolteAllowedListInterface> allowedList,
+ std::unique_ptr<VolteCapabilityCellularInterface> cellularInterface)
+ : imsiParser(std::move(imsiParser)), allowedList(std::move(allowedList)),
+ cellularInterface(std::move(cellularInterface))
+ {}
+
+ void VolteCapabilityHandler::setChannel(at::BaseChannel *channel)
+ {
+ if (cellularInterface.get() != nullptr) {
+ cellularInterface->setChannel(channel);
+ }
+ }
+
+ auto VolteCapabilityHandler::isVolteAllowed() -> bool
+ {
+ const auto imsi = cellularInterface->getImsi();
+ if (not imsi.has_value()) {
+ LOG_ERROR("Failed to read IMSI, disable VoLTE.");
+ return false;
+ }
+
+ const auto operatorInfo = imsiParser->parse(imsi.value());
+ if (not operatorInfo.has_value()) {
+ LOG_ERROR("Failed to parse IMSI, disable VoLTE.");
+ return false;
+ }
+ return allowedList->isVolteAllowed(operatorInfo.value());
+ }
+
+ auto VolteCapabilityHandler::isCellularInterfaceReady() -> bool
+ {
+ return cellularInterface.get() != nullptr;
+ }
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp => module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp +43 -0
@@ 0,0 1,43 @@
+// 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 "VolteCapabiltyHandlerCellularInterface.hpp"
+#include "ImsiParserInterface.hpp"
+#include "VolteAllowedListInterface.hpp"
+
+#include <memory>
+#include <vector>
+
+namespace at
+{
+ class Result;
+ class BaseChannel;
+} // namespace at
+
+namespace cellular::service
+{
+ class VolteCapabilityHandler
+ {
+ public:
+ VolteCapabilityHandler(std::unique_ptr<ImsiParserInteface> imsiParser,
+ std::unique_ptr<VolteAllowedListInterface> allowedList,
+ std::unique_ptr<VolteCapabilityCellularInterface> cellularInterface);
+ /** Set AT command channel
+ * \param channel channel (or nullptr to block communication):
+ */
+ void setChannel(at::BaseChannel *channel);
+ /** Check if it is a possibility to enable VoLTE on current operator
+ * @return true when VoLTE is allowed, false when not
+ */
+ auto isVolteAllowed() -> bool;
+
+ private:
+ std::unique_ptr<ImsiParserInteface> imsiParser;
+ std::unique_ptr<VolteAllowedListInterface> allowedList;
+ std::unique_ptr<VolteCapabilityCellularInterface> cellularInterface;
+
+ auto isCellularInterfaceReady() -> bool;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteCapabilityHandlerCellular.cpp => module-services/service-cellular/src/volte/VolteCapabilityHandlerCellular.cpp +29 -0
@@ 0,0 1,29 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "VolteCapabilityHandlerCellular.hpp"
+#include <modem/mux/CellularMux.h>
+
+namespace cellular::service
+{
+ void VolteCapabilityCellular::setChannel(at::BaseChannel *channel)
+ {
+ this->channel = channel;
+ }
+
+ auto VolteCapabilityCellular::getImsi() -> std::optional<std::string>
+ {
+ if (channel == nullptr) {
+ LOG_ERROR("No channel provided. Request ignored");
+ return std::nullopt;
+ }
+
+ auto result = channel->cmd(at::AT::CIMI);
+ if (not result) {
+ LOG_ERROR("Failed to read IMSI, disable VoLTE.");
+ return std::nullopt;
+ }
+
+ return result.response[0];
+ }
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteCapabilityHandlerCellular.hpp => module-services/service-cellular/src/volte/VolteCapabilityHandlerCellular.hpp +29 -0
@@ 0,0 1,29 @@
+// 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 "VolteCapabiltyHandlerCellularInterface.hpp"
+
+#include <optional>
+#include <string>
+
+namespace at
+{
+ class Result;
+ class BaseChannel;
+} // namespace at
+
+namespace cellular::service
+{
+
+ class VolteCapabilityCellular : public VolteCapabilityCellularInterface
+ {
+ public:
+ void setChannel(at::BaseChannel *channel) final;
+ auto getImsi() -> std::optional<std::string> final;
+
+ private:
+ at::BaseChannel *channel = nullptr;
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/volte/VolteCapabiltyHandlerCellularInterface.hpp => module-services/service-cellular/src/volte/VolteCapabiltyHandlerCellularInterface.hpp +25 -0
@@ 0,0 1,25 @@
+// 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 <optional>
+#include <string>
+
+namespace at
+{
+ class Result;
+ class BaseChannel;
+} // namespace at
+
+namespace cellular::service
+{
+
+ class VolteCapabilityCellularInterface
+ {
+ public:
+ virtual ~VolteCapabilityCellularInterface() = default;
+ virtual void setChannel(at::BaseChannel *channel) = 0;
+ virtual auto getImsi() -> std::optional<std::string> = 0;
+ };
+} // namespace cellular::service
M module-services/service-cellular/tests/CMakeLists.txt => module-services/service-cellular/tests/CMakeLists.txt +9 -0
@@ 113,3 113,12 @@ add_catch2_executable(
module-cellular
module-utils
)
+
+add_catch2_executable(
+ NAME
+ cellular-volte-capability-handler
+ SRCS
+ unittest_volteCapabilityHandler.cpp
+ LIBS
+ module-cellular
+)
A module-services/service-cellular/tests/unittest_volteCapabilityHandler.cpp => module-services/service-cellular/tests/unittest_volteCapabilityHandler.cpp +146 -0
@@ 0,0 1,146 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <catch2/catch.hpp>
+
+#include <module-services/service-cellular/src/volte/VolteCapabilityHandler.hpp>
+#include <module-services/service-cellular/src/volte/ImsiParserUS.hpp>
+#include <module-services/service-cellular/src/volte/VolteAllowedUSList.hpp>
+#include <module-services/service-cellular/src/volte/VolteCapabiltyHandlerCellularInterface.hpp>
+
+TEST_CASE("VoLTE Capability handler")
+{
+ SECTION("ImsiParserUS success - US IMSI")
+ {
+ using namespace cellular::service;
+
+ cellular::service::ImsiParserUS parser;
+ std::string imsi("3111231234567890");
+
+ auto result = parser.parse(imsi);
+
+ REQUIRE(result.has_value() == true);
+ REQUIRE(result.value().MCC == "311");
+ REQUIRE(result.value().MNC == "123");
+ }
+
+ SECTION("ImsiParserUS failure - non US IMSI")
+ {
+ using namespace cellular::service;
+
+ cellular::service::ImsiParserUS parser;
+ std::string imsi("2601231234567890");
+
+ auto result = parser.parse(imsi);
+
+ REQUIRE(result.has_value() == false);
+ }
+
+ SECTION("ImsiParserUS failure -too short IMSI")
+ {
+ using namespace cellular::service;
+
+ cellular::service::ImsiParserUS parser;
+ std::string imsi("2601");
+
+ auto result = parser.parse(imsi);
+
+ REQUIRE(result.has_value() == false);
+ }
+
+ SECTION("VolteAllowedUSList success - TMobileUS IMSI")
+ {
+ using namespace cellular::service;
+
+ VolteAllowedUSList list;
+ OperatorInfo operatorInfo{"310", "120"};
+
+ auto result = list.isVolteAllowed(operatorInfo);
+
+ REQUIRE(result == true);
+ }
+
+ SECTION("VolteAllowedUSList failure - non TMobileUS MCC")
+ {
+ using namespace cellular::service;
+
+ VolteAllowedUSList list;
+ OperatorInfo operatorInfo{"210", "120"};
+
+ auto result = list.isVolteAllowed(operatorInfo);
+
+ REQUIRE(result == false);
+ }
+
+ SECTION("VolteAllowedUSList failure - non TMobileUS MNC")
+ {
+ using namespace cellular::service;
+
+ VolteAllowedUSList list;
+ OperatorInfo operatorInfo{"310", "999"};
+
+ auto result = list.isVolteAllowed(operatorInfo);
+
+ REQUIRE(result == false);
+ }
+
+ class MockTMobileUS : public cellular::service::VolteCapabilityCellularInterface
+ {
+ void setChannel(at::BaseChannel *channel)
+ {}
+ auto getImsi() -> std::optional<std::string>
+ {
+ return "310120123456789";
+ }
+ };
+
+ SECTION("VolteCapabilityHandler succes = TMobileUS")
+ {
+ using namespace cellular::service;
+ VolteCapabilityHandler handler{std::make_unique<ImsiParserUS>(),
+ std::make_unique<VolteAllowedUSList>(),
+ std::make_unique<MockTMobileUS>()};
+ auto result = handler.isVolteAllowed();
+ REQUIRE(result == true);
+ }
+
+ class MockNonTMobileUS : public cellular::service::VolteCapabilityCellularInterface
+ {
+ void setChannel(at::BaseChannel *channel)
+ {}
+ auto getImsi() -> std::optional<std::string>
+ {
+ return "310999123456789";
+ }
+ };
+
+ SECTION("VolteCapabilityHandler failure = non TMobileUS")
+ {
+ using namespace cellular::service;
+ VolteCapabilityHandler handler{std::make_unique<ImsiParserUS>(),
+ std::make_unique<VolteAllowedUSList>(),
+ std::make_unique<MockNonTMobileUS>()};
+ auto result = handler.isVolteAllowed();
+ REQUIRE(result == false);
+ }
+
+ class MockFailedToGetImsi : public cellular::service::VolteCapabilityCellularInterface
+ {
+ void setChannel(at::BaseChannel *channel)
+ {}
+ auto getImsi() -> std::optional<std::string>
+ {
+ return std::nullopt;
+ }
+ };
+
+ SECTION("VolteCapabilityHandler failure = failed to get imsi")
+ {
+ using namespace cellular::service;
+ VolteCapabilityHandler handler{std::make_unique<ImsiParserUS>(),
+ std::make_unique<VolteAllowedUSList>(),
+ std::make_unique<MockFailedToGetImsi>()};
+ auto result = handler.isVolteAllowed();
+ REQUIRE(result == false);
+ }
+}