From 091e76ae7c1c70856c3d05ac2cbfa6c99de7b9c1 Mon Sep 17 00:00:00 2001 From: Kuba Date: Thu, 19 Aug 2021 16:24:37 +0200 Subject: [PATCH] [EGD-6914] Fix missing no sim message Added QSIMSTAT urc handler and command parser. Added sim insertion state in SimCard. Now there No Sim response is sended if sim is missing. Sim not respond timer removed from Application Manager. --- .../locks/handlers/SimLockHandler.cpp | 16 ++-- .../locks/handlers/SimLockHandler.hpp | 4 +- module-cellular/CMakeLists.txt | 2 + module-cellular/at/SimInsertedState.hpp | 20 +++++ module-cellular/at/UrcHandler.hpp | 2 + module-cellular/at/UrcQSimstat.hpp | 40 ++++++++++ module-cellular/at/cmd/QSIMSTAT.hpp | 43 ++++++++++ module-cellular/at/cmd/src/QSIMSTAT.cpp | 80 +++++++++++++++++++ module-cellular/at/src/UrcFactory.cpp | 4 + module-cellular/at/src/UrcQSimstat.cpp | 29 +++++++ .../test/mock/AtCommon_channel.hpp | 80 +++++++++++++++++++ module-cellular/test/unittest_URC.cpp | 55 +++++++++++++ .../test/unittest_parse_result.cpp | 73 +++++++++++++++++ .../service-cellular/CellularUrcHandler.cpp | 11 +++ .../service-cellular/CellularUrcHandler.hpp | 2 + .../service-cellular/ServiceCellular.cpp | 3 +- .../api/notification/notification.hpp | 3 + .../service-cellular/CellularMessage.hpp | 16 ++++ .../service-cellular/ServiceCellular.hpp | 1 + .../src/ServiceCellularPriv.cpp | 34 +++++++- .../service-cellular/src/SimCard.cpp | 50 ++++++++++++ .../service-cellular/src/SimCard.hpp | 72 +++++++++++++++++ .../service-fota/FotaUrcHandler.hpp | 1 + .../services/appmgr/ApplicationManager.cpp | 3 + 24 files changed, 629 insertions(+), 15 deletions(-) create mode 100644 module-cellular/at/SimInsertedState.hpp create mode 100644 module-cellular/at/UrcQSimstat.hpp create mode 100644 module-cellular/at/cmd/QSIMSTAT.hpp create mode 100644 module-cellular/at/cmd/src/QSIMSTAT.cpp create mode 100644 module-cellular/at/src/UrcQSimstat.cpp diff --git a/module-apps/apps-common/locks/handlers/SimLockHandler.cpp b/module-apps/apps-common/locks/handlers/SimLockHandler.cpp index 131fbc983e03871716f17e124aeeabb5fcb8f633..8ec3325a15847ca76c612aeb77551bc75eba0c31 100644 --- a/module-apps/apps-common/locks/handlers/SimLockHandler.cpp +++ b/module-apps/apps-common/locks/handlers/SimLockHandler.cpp @@ -17,17 +17,11 @@ namespace locks constexpr unsigned int default_attempts = 4; constexpr unsigned int max_input_size = 8; constexpr unsigned int min_input_size = 4; - constexpr unsigned int sim_not_responding_timeout = 3; SimLockHandler::SimLockHandler(sys::Service *owner) : owner(owner), lock(Lock::LockState::Unlocked, default_attempts) { lock.setInputSizeBounds(min_input_size, max_input_size); - - simResponseTimer = sys::TimerFactory::createSingleShotTimer( - owner, simResponseTimerName, std::chrono::seconds{sim_not_responding_timeout}, [this](sys::Timer &) { - handleSimNotRespondingMessage(); - }); } void SimLockHandler::clearStoredInputs() @@ -38,8 +32,6 @@ namespace locks void SimLockHandler::setSimInputTypeAction(SimInputTypeAction _simInputTypeAction) { - simResponseTimer.stop(); - if (simInputTypeAction != _simInputTypeAction) { simInputTypeAction = _simInputTypeAction; lock.lockState = Lock::LockState::Unlocked; @@ -107,7 +99,6 @@ namespace locks void SimLockHandler::setSim(cellular::api::SimSlot simSlot) { if (simReady) { - simResponseTimer.start(); Store::GSM::get()->selected = static_cast(simSlot); owner->bus.sendUnicast(simSlot); } @@ -271,10 +262,15 @@ namespace locks sys::MessagePointer SimLockHandler::handleSimReadyMessage() { - simResponseTimer.stop(); + setSimReady(); return sys::msgHandled(); } + sys::MessagePointer SimLockHandler::handleSimNotInsertedMessage() + { + return handleSimNotRespondingMessage(); + } + sys::MessagePointer SimLockHandler::handleSimNotRespondingMessage() { setSimInputTypeAction(SimInputTypeAction::Error); diff --git a/module-apps/apps-common/locks/handlers/SimLockHandler.hpp b/module-apps/apps-common/locks/handlers/SimLockHandler.hpp index 36931fa2186427ce6b5a10ee5b11bc34e634683a..77f9f833052957c701ba845656ab65b84d19784b 100644 --- a/module-apps/apps-common/locks/handlers/SimLockHandler.hpp +++ b/module-apps/apps-common/locks/handlers/SimLockHandler.hpp @@ -13,7 +13,6 @@ namespace locks { using StoredLockInput = std::vector; - constexpr auto simResponseTimerName = "SimResponseTimer"; class SimLockHandler { @@ -28,8 +27,6 @@ namespace locks StoredLockInput storedFirstInput; StoredLockInput storedSecondInput; - sys::TimerHandle simResponseTimer; - void clearStoredInputs(); void setSimInputTypeAction(SimInputTypeAction _simInputTypeAction); @@ -70,6 +67,7 @@ namespace locks sys::MessagePointer handleSimPinChangedMessage(); sys::MessagePointer handleSimAvailabilityMessage(); sys::MessagePointer handleSimReadyMessage(); + sys::MessagePointer handleSimNotInsertedMessage(); sys::MessagePointer handleSimNotRespondingMessage(); void getSettingsSimSelect(const std::string &settingsSim); diff --git a/module-cellular/CMakeLists.txt b/module-cellular/CMakeLists.txt index f2267ba0a65c72171e4eb02489eaa0abb06e4fee..d38b57e390035c90afb05ae3e4eb02a5d3c5924f 100644 --- a/module-cellular/CMakeLists.txt +++ b/module-cellular/CMakeLists.txt @@ -24,6 +24,7 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcQiurc.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcPoweredDown.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcRing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcQSimstat.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcResponse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/src/Commands.cpp @@ -37,6 +38,7 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CPBS.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/CPBR.cpp ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QNWINFO.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/at/cmd/src/QSIMSTAT.cpp ) add_library(${PROJECT_NAME} STATIC ${SOURCES}) diff --git a/module-cellular/at/SimInsertedState.hpp b/module-cellular/at/SimInsertedState.hpp new file mode 100644 index 0000000000000000000000000000000000000000..da066009de173e69a58d627fe6b4acbd40e8a380 --- /dev/null +++ b/module-cellular/at/SimInsertedState.hpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +namespace at +{ + enum class SimInsertedStatus + { + Removed, + Inserted, + Unknown + }; + + enum class SimInsertedStatusEnable + { + Disable, + Enable + }; +} // namespace at diff --git a/module-cellular/at/UrcHandler.hpp b/module-cellular/at/UrcHandler.hpp index bd64350f06a9358c0c9859570404ab67421ed015..6a6943e1e25682f482f65b40f7d4913b9dc78327 100644 --- a/module-cellular/at/UrcHandler.hpp +++ b/module-cellular/at/UrcHandler.hpp @@ -16,6 +16,7 @@ namespace at::urc class Ring; class PoweredDown; class UrcResponse; + class QSimstat; class UrcHandler { @@ -31,5 +32,6 @@ namespace at::urc virtual void Handle(Ring &urc) = 0; virtual void Handle(PoweredDown &urc) = 0; virtual void Handle(UrcResponse &urc) = 0; + virtual void Handle(QSimstat &urc) = 0; }; } // namespace at::urc diff --git a/module-cellular/at/UrcQSimstat.hpp b/module-cellular/at/UrcQSimstat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e2e5d0b0df52ed5d468f75d41fbfb31f7dadf59b --- /dev/null +++ b/module-cellular/at/UrcQSimstat.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include "Urc.hpp" +#include "UrcHandler.hpp" +#include "SimInsertedState.hpp" + +namespace at::urc +{ + class QSimstat : public Urc + { + static constexpr std::string_view head = "+QSIMSTAT"; + const size_t minParametersCount = 2; + + enum class Tokens + { + Enable, + InsertedStatus + }; + + public: + static bool isURC(const std::string &uHead) + { + return uHead.find(QSimstat::head) != std::string::npos; + } + + using Urc::Urc; + + [[nodiscard]] auto isValid() const noexcept -> bool override; + [[nodiscard]] auto getInsertedStatus() const noexcept -> std::optional; + [[nodiscard]] auto getEnabled() const noexcept -> std::optional; + + void Handle(UrcHandler &h) final + { + h.Handle(*this); + } + }; +} // namespace at::urc diff --git a/module-cellular/at/cmd/QSIMSTAT.hpp b/module-cellular/at/cmd/QSIMSTAT.hpp new file mode 100644 index 0000000000000000000000000000000000000000..22832e45cf43d64a773b7e3496db0c28fe196a64 --- /dev/null +++ b/module-cellular/at/cmd/QSIMSTAT.hpp @@ -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 "Result.hpp" +#include +#include +#include + +namespace at +{ + namespace result + { + struct QSIMSTAT : public Result + { + at::SimInsertedStatusEnable enabled; + at::SimInsertedStatus status; + explicit QSIMSTAT(const Result &that); + }; + } // namespace result + + namespace cmd + { + class QSIMSTAT : public Cmd + { + public: + QSIMSTAT() noexcept; + explicit QSIMSTAT(at::cmd::Modifier mod) noexcept; + + [[nodiscard]] auto parseQSIMSTAT(const Result &base_result) -> result::QSIMSTAT; + + private: + enum class responseTokens + { + Enabled, + SimInserted + }; + void parseTokens(const std::vector &tokens, result::QSIMSTAT &parsed); + }; + } // namespace cmd + +} // namespace at diff --git a/module-cellular/at/cmd/src/QSIMSTAT.cpp b/module-cellular/at/cmd/src/QSIMSTAT.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82446cef7948176655f47bb1bb184405190d3c7f --- /dev/null +++ b/module-cellular/at/cmd/src/QSIMSTAT.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include + +#include +#include +#include + +namespace at +{ + namespace cmd + { + QSIMSTAT::QSIMSTAT(at::cmd::Modifier mod) noexcept : Cmd("AT+QSIMSTAT", mod, at::default_timeout) + {} + + QSIMSTAT::QSIMSTAT() noexcept : QSIMSTAT(at::cmd::Modifier::None) + {} + + auto QSIMSTAT::parseQSIMSTAT(const Result &base_result) -> result::QSIMSTAT + { + + auto constexpr responseHeader = "+QSIMSTAT: "; + auto constexpr qsimstatTokensCount = 2; + result::QSIMSTAT parsed{base_result}; + + if (parsed) { + if (parsed.response.empty()) { + LOG_ERROR("Can't parse - empty response"); + parsed.code = result::QSIMSTAT::Code::PARSING_ERROR; + } + else { + std::string str = parsed.response[0]; + if (str.find(responseHeader) == std::string::npos) { + LOG_ERROR("Can't parse - bad header"); + parsed.code = result::QSIMSTAT::Code::PARSING_ERROR; + return parsed; + } + + utils::findAndReplaceAll(str, responseHeader, ""); + utils::trim(str); + + std::vector tokens = utils::split(str, ','); + + if (tokens.size() != qsimstatTokensCount) { + LOG_ERROR("Can't parse - invalid tokens count"); + parsed.code = result::QSIMSTAT::Code::PARSING_ERROR; + return parsed; + } + + parseTokens(tokens, parsed); + } + } + return parsed; + } + + void QSIMSTAT::parseTokens(const std::vector &tokens, result::QSIMSTAT &parsed) + { + auto status = 0, enabled = 0; + if (utils::toNumeric(tokens[magic_enum::enum_integer(responseTokens::SimInserted)], status) && + utils::toNumeric(tokens[magic_enum::enum_integer(responseTokens::Enabled)], enabled)) { + if (magic_enum::enum_contains(status) && + magic_enum::enum_contains(enabled)) { + parsed.enabled = static_cast(enabled); + parsed.status = static_cast(status); + return; + } + } + + LOG_ERROR("Can't parse - bad value"); + parsed.code = result::QSIMSTAT::Code::PARSING_ERROR; + } + } // namespace cmd + namespace result + { + QSIMSTAT::QSIMSTAT(const Result &that) : Result(that) + {} + + } // namespace result +} // namespace at diff --git a/module-cellular/at/src/UrcFactory.cpp b/module-cellular/at/src/UrcFactory.cpp index d68ce0cfd7900f59d49aeb132cc2f048642ec01f..cbfdf6afa89fbe3679563cd4a09a0a2f88641433 100644 --- a/module-cellular/at/src/UrcFactory.cpp +++ b/module-cellular/at/src/UrcFactory.cpp @@ -14,6 +14,7 @@ #include #include #include +#include using namespace at::urc; @@ -60,6 +61,9 @@ std::unique_ptr UrcFactory::Create(const std::string &urcMessage) else if (Qiurc::isURC(head)) { return std::make_unique(body); } + else if (QSimstat::isURC(head)) { + return std::make_unique(body); + } return std::make_unique(body, head); } diff --git a/module-cellular/at/src/UrcQSimstat.cpp b/module-cellular/at/src/UrcQSimstat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..051b05b2e38d106dcbc2822b6fc787893ed77097 --- /dev/null +++ b/module-cellular/at/src/UrcQSimstat.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "UrcQSimstat.hpp" + +using namespace at::urc; + +auto QSimstat::isValid() const noexcept -> bool +{ + return tokens.size() >= minParametersCount; +} +auto QSimstat::getInsertedStatus() const noexcept -> std::optional +{ + auto status = 0; + if (utils::toNumeric(tokens[magic_enum::enum_integer(Tokens::InsertedStatus)], status) && + magic_enum::enum_contains(status)) { + return static_cast(status); + } + return std::nullopt; +} +auto QSimstat::getEnabled() const noexcept -> std::optional +{ + auto enabled = 0; + if (utils::toNumeric(tokens[magic_enum::enum_integer(Tokens::Enable)], enabled) && + magic_enum::enum_contains(enabled)) { + return static_cast(enabled); + } + return std::nullopt; +} diff --git a/module-cellular/test/mock/AtCommon_channel.hpp b/module-cellular/test/mock/AtCommon_channel.hpp index f919ff09ec1870f65f29d07976199fe7c7dd4d6d..1e93de37a0bd833b3cd3adffcff13551f58ba823 100644 --- a/module-cellular/test/mock/AtCommon_channel.hpp +++ b/module-cellular/test/mock/AtCommon_channel.hpp @@ -429,4 +429,84 @@ namespace at return result; } }; + + /// provides proper QSIMSTAT response + class QSIMSTAT_successChannel : public ChannelMock + { + public: + const std::string enabled = "1"; + const std::string status = "1"; + + auto ResultMock() -> Result final + { + auto result = Result(); + result.code = Result::Code::OK; + result.response = {"+QSIMSTAT: " + enabled + "," + status, "OK"}; + return result; + } + }; + + /// provides invalid QSIMSTAT response + class QSIMSTAT_toLittleTokens : public ChannelMock + { + public: + const std::string enabled = "1"; + + auto ResultMock() -> Result final + { + auto result = Result(); + result.code = Result::Code::OK; + result.response = {"+QSIMSTAT: " + enabled, "OK"}; + return result; + } + }; + + /// provides invalid QSIMSTAT response + class QSIMSTAT_toManyTokens : public ChannelMock + { + public: + const std::string enabled = "1"; + const std::string status = "1"; + const std::string bad = "1"; + + auto ResultMock() -> Result final + { + auto result = Result(); + result.code = Result::Code::OK; + result.response = {"+QSIMSTAT: " + enabled + "," + status + "," + bad, "OK"}; + return result; + } + }; + + /// provides invalid QSIMSTAT response + class QSIMSTAT_invalidEnabled : public ChannelMock + { + public: + const std::string enabled = "78"; + const std::string status = "1"; + + auto ResultMock() -> Result final + { + auto result = Result(); + result.code = Result::Code::OK; + result.response = {"+QSIMSTAT: " + enabled + "," + status, "OK"}; + return result; + } + }; + + /// provides invalid QSIMSTAT response + class QSIMSTAT_invalidStatus : public ChannelMock + { + public: + const std::string enabled = "1"; + const std::string status = "98"; + + auto ResultMock() -> Result final + { + auto result = Result(); + result.code = Result::Code::OK; + result.response = {"+QSIMSTAT: " + enabled + "," + status, "OK"}; + return result; + } + }; } // namespace at diff --git a/module-cellular/test/unittest_URC.cpp b/module-cellular/test/unittest_URC.cpp index f682f2e058773de3751351efec27ad17c24080af..d9794cf133014fb1d43715883d55f72ee69d3d08 100644 --- a/module-cellular/test/unittest_URC.cpp +++ b/module-cellular/test/unittest_URC.cpp @@ -22,8 +22,10 @@ #include "UrcRing.hpp" #include "UrcPoweredDown.hpp" #include "UrcResponse.hpp" +#include #include "UrcFactory.hpp" #include "SimState.hpp" +#include template static auto getURC(std::unique_ptr &urc) -> std::shared_ptr { @@ -925,3 +927,56 @@ TEST_CASE("RING") } } } + +TEST_CASE("QSimstat") +{ + SECTION("Valid parameter, inserted") + { + auto urc = at::urc::UrcFactory::Create("+QSIMSTAT: 1,1"); + auto qsimstat = getURC(urc); + + REQUIRE(qsimstat); + REQUIRE(qsimstat->getInsertedStatus() == at::SimInsertedStatus::Inserted); + REQUIRE(qsimstat->getEnabled() == at::SimInsertedStatusEnable::Enable); + } + + SECTION("Valid parameter, removed") + { + auto urc = at::urc::UrcFactory::Create("+QSIMSTAT: 1,0"); + auto qsimstat = getURC(urc); + + REQUIRE(qsimstat); + REQUIRE(qsimstat->getInsertedStatus() == at::SimInsertedStatus::Removed); + REQUIRE(qsimstat->getEnabled() == at::SimInsertedStatusEnable::Enable); + } + + SECTION("Valid parameter, unknown") + { + auto urc = at::urc::UrcFactory::Create("+QSIMSTAT: 0,2"); + auto qsimstat = getURC(urc); + + REQUIRE(qsimstat); + REQUIRE(qsimstat->getInsertedStatus() == at::SimInsertedStatus::Unknown); + REQUIRE(qsimstat->getEnabled() == at::SimInsertedStatusEnable::Disable); + } + + SECTION("Invalid parameter, wrong Enable token") + { + auto urc = at::urc::UrcFactory::Create("+QSIMSTAT: 5,2"); + auto qsimstat = getURC(urc); + + REQUIRE(qsimstat); + REQUIRE(qsimstat->getInsertedStatus() == at::SimInsertedStatus::Unknown); + REQUIRE(qsimstat->getEnabled() == std::nullopt); + } + + SECTION("Invalid parameter, wrong Inserted Status token") + { + auto urc = at::urc::UrcFactory::Create("+QSIMSTAT: 0,7"); + auto qsimstat = getURC(urc); + + REQUIRE(qsimstat); + REQUIRE(qsimstat->getInsertedStatus() == std::nullopt); + REQUIRE(qsimstat->getEnabled() == at::SimInsertedStatusEnable::Disable); + } +} diff --git a/module-cellular/test/unittest_parse_result.cpp b/module-cellular/test/unittest_parse_result.cpp index 888b4b69802ca3c6345c03d6ee3173f6c64f097b..ff90742be1bbc7f414d981aa8136890208729bbb 100644 --- a/module-cellular/test/unittest_parse_result.cpp +++ b/module-cellular/test/unittest_parse_result.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "mock/AtCommon_channel.hpp" #include "PhoneNumber.hpp" @@ -681,3 +682,75 @@ TEST_CASE("QNWINFO parser test") REQUIRE(!resp); } } + +TEST_CASE("QSIMSTAT parser") +{ + SECTION("Empty data") + { + at::cmd::QSIMSTAT cmd; + at::Result result; + auto response = cmd.parseQSIMSTAT(result); + REQUIRE(!response); + } + + SECTION("Failing channel") + { + at::cmd::QSIMSTAT cmd; + at::FailingChannel channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(!response); + REQUIRE(response.code == at::Result::Code::ERROR); + } + + SECTION("Success - valid token") + { + at::cmd::QSIMSTAT cmd; + at::QSIMSTAT_successChannel channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(response); + REQUIRE(response.enabled == at::SimInsertedStatusEnable::Enable); + REQUIRE(response.status == at::SimInsertedStatus::Inserted); + } + + SECTION("to little tokens") + { + at::cmd::QSIMSTAT cmd; + at::QSIMSTAT_toLittleTokens channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(!response); + REQUIRE(response.code == at::Result::Code::PARSING_ERROR); + } + + SECTION("Failed - to many tokens") + { + at::cmd::QSIMSTAT cmd; + at::QSIMSTAT_toManyTokens channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(!response); + REQUIRE(response.code == at::Result::Code::PARSING_ERROR); + } + + SECTION("Failed - invalid enabled oken") + { + at::cmd::QSIMSTAT cmd; + at::QSIMSTAT_invalidEnabled channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(!response); + REQUIRE(response.code == at::Result::Code::PARSING_ERROR); + } + + SECTION("Failed - invalid status token") + { + at::cmd::QSIMSTAT cmd; + at::QSIMSTAT_invalidStatus channel; + auto base = channel.cmd(cmd); + auto response = cmd.parseQSIMSTAT(base); + REQUIRE(!response); + REQUIRE(response.code == at::Result::Code::PARSING_ERROR); + } +} diff --git a/module-services/service-cellular/CellularUrcHandler.cpp b/module-services/service-cellular/CellularUrcHandler.cpp index 6e0ce3a79d77fbed53c6ab3b0c6ee04288fc7b76..2fac87fd601279e6f6718050c968c28e55a43654 100644 --- a/module-services/service-cellular/CellularUrcHandler.cpp +++ b/module-services/service-cellular/CellularUrcHandler.cpp @@ -210,3 +210,14 @@ void CellularUrcHandler::Handle(UrcResponse &urc) } } } + +void CellularUrcHandler::Handle(QSimstat &urc) +{ + if (urc.isValid()) { + auto inserted = urc.getInsertedStatus(); + if (inserted.has_value()) { + response = std::make_unique(inserted.value()); + urc.setHandled(true); + } + } +} diff --git a/module-services/service-cellular/CellularUrcHandler.hpp b/module-services/service-cellular/CellularUrcHandler.hpp index 47e87cff8e743c7727d371f7085634b9c3d5edd9..6e7622c4b6c603dba844ddc5001aac4609d871ba 100644 --- a/module-services/service-cellular/CellularUrcHandler.hpp +++ b/module-services/service-cellular/CellularUrcHandler.hpp @@ -18,6 +18,7 @@ #include #include #include +#include /** * ServiceCellular helper for handling Urc messages @@ -39,6 +40,7 @@ class CellularUrcHandler : public at::urc::UrcHandler void Handle(at::urc::Ring &urc) final; void Handle(at::urc::PoweredDown &urc) final; void Handle(at::urc::UrcResponse &urc) final; + void Handle(at::urc::QSimstat &urc) final; /** * Gets the response that should be returned after handling Urc diff --git a/module-services/service-cellular/ServiceCellular.cpp b/module-services/service-cellular/ServiceCellular.cpp index 5e0c17bf7c93d84ce630e04673141b397108f38e..97e327ad29c7bee3eba6cc7a9d64f955624ad89f 100644 --- a/module-services/service-cellular/ServiceCellular.cpp +++ b/module-services/service-cellular/ServiceCellular.cpp @@ -143,6 +143,8 @@ ServiceCellular::ServiceCellular() connectionManager->onTimerTick(); }); }); + simTimer = sys::TimerFactory::createSingleShotTimer( + this, "simTimer", std::chrono::milliseconds{6000}, [this](sys::Timer &) { priv->simCard->handleSimTimer(); }); ongoingCall.setStartCallAction([=](const CalllogRecord &rec) { auto call = DBServiceAPI::CalllogAdd(this, rec); @@ -1267,7 +1269,6 @@ bool ServiceCellular::handle_sim_sanity_check() auto ret = sim_check_hot_swap(cmux->get(CellularMux::Channel::Commands)); if (ret) { priv->state->set(State::ST::ModemOn); - bsp::cellular::sim::simSelect(); } else { LOG_ERROR("Sanity check failure - user will be promped about full shutdown"); diff --git a/module-services/service-cellular/include/service-cellular/api/notification/notification.hpp b/module-services/service-cellular/include/service-cellular/api/notification/notification.hpp index baa9eb4ff19d2c365502eab59e9e8e5330703576..85b2509c81e74b2df0ad81e4061d7e4cb5b54365 100644 --- a/module-services/service-cellular/include/service-cellular/api/notification/notification.hpp +++ b/module-services/service-cellular/include/service-cellular/api/notification/notification.hpp @@ -45,4 +45,7 @@ namespace cellular::msg::notification const api::ModemState state; }; + struct SimNotInserted : public msg::Notification + {}; + } // namespace cellular::msg::notification diff --git a/module-services/service-cellular/service-cellular/CellularMessage.hpp b/module-services/service-cellular/service-cellular/CellularMessage.hpp index c6da4002148dd2232b28fce609342ec061f0e026..83b338cdaf2e028f0dd05459e3420c787f47279b 100644 --- a/module-services/service-cellular/service-cellular/CellularMessage.hpp +++ b/module-services/service-cellular/service-cellular/CellularMessage.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -960,4 +961,19 @@ namespace cellular std::shared_ptr imei; }; + class SimInsertedNotication : public sys::DataMessage + { + public: + explicit SimInsertedNotication(at::SimInsertedStatus status) + : sys::DataMessage(MessageType::MessageTypeUninitialized), insertedStatus(status) + {} + auto getInsertedStatus() -> at::SimInsertedStatus + { + return insertedStatus; + } + + private: + at::SimInsertedStatus insertedStatus; + }; + } // namespace cellular diff --git a/module-services/service-cellular/service-cellular/ServiceCellular.hpp b/module-services/service-cellular/service-cellular/ServiceCellular.hpp index 76f56afd8aabd69bed3796449943d727ac5c3ac1..00139a84ef3c9d5dc4212b719d9285efd86c2272 100644 --- a/module-services/service-cellular/service-cellular/ServiceCellular.hpp +++ b/module-services/service-cellular/service-cellular/ServiceCellular.hpp @@ -112,6 +112,7 @@ class ServiceCellular : public sys::Service sys::TimerHandle callEndedRecentlyTimer; sys::TimerHandle stateTimer; sys::TimerHandle ussdTimer; + sys::TimerHandle simTimer; // used to enter modem sleep mode sys::TimerHandle sleepTimer; diff --git a/module-services/service-cellular/src/ServiceCellularPriv.cpp b/module-services/service-cellular/src/ServiceCellularPriv.cpp index c9382db02a7dc6793b51f68e63cb96422d920fd4..98e484520dd6da2c73bd65c91d06a7eb47119309 100644 --- a/module-services/service-cellular/src/ServiceCellularPriv.cpp +++ b/module-services/service-cellular/src/ServiceCellularPriv.cpp @@ -52,6 +52,7 @@ namespace cellular::internal simCard->onUnhandledCME = [this](unsigned int code) { owner->bus.sendMulticast(code); }; + simCard->onSimNotPresent = [this]() { owner->bus.sendMulticast(); }; } void ServiceCellularPriv::connectSimCard() @@ -62,27 +63,49 @@ namespace cellular::internal */ owner->connect(typeid(request::sim::SetActiveSim), [&](sys::Message *request) -> sys::MessagePointer { auto msg = static_cast(request); - return std::make_shared(simCard->handleSetActiveSim(msg->sim)); + auto result = simCard->handleSetActiveSim(msg->sim); + owner->simTimer.start(); + return std::make_shared(result); }); owner->connect(typeid(request::sim::GetLockState), [&](sys::Message *) -> sys::MessagePointer { + if (!simCard->isSimCardInserted()) { + owner->bus.sendMulticast(); + return sys::MessageNone{}; + } return std::make_shared(simCard->handleIsPinLocked()); }); owner->connect(typeid(request::sim::ChangePin), [&](sys::Message *request) -> sys::MessagePointer { auto msg = static_cast(request); + if (!simCard->isSimCardInserted()) { + owner->bus.sendMulticast(); + return sys::MessageNone{}; + } return std::make_shared(simCard->handleChangePin(msg->oldPin, msg->pin)); }); owner->connect(typeid(request::sim::UnblockWithPuk), [&](sys::Message *request) -> sys::MessagePointer { auto msg = static_cast(request); + if (!simCard->isSimCardInserted()) { + owner->bus.sendMulticast(); + return sys::MessageNone{}; + } return std::make_shared( simCard->handleUnblockWithPuk(msg->puk, msg->pin)); }); owner->connect(typeid(request::sim::SetPinLock), [&](sys::Message *request) -> sys::MessagePointer { auto msg = static_cast(request); + if (!simCard->isSimCardInserted()) { + owner->bus.sendMulticast(); + return sys::MessageNone{}; + } return std::make_shared(simCard->handleSetPinLock(msg->pin, msg->lock), msg->lock); }); owner->connect(typeid(request::sim::PinUnlock), [&](sys::Message *request) -> sys::MessagePointer { auto msg = static_cast(request); + if (!simCard->isSimCardInserted()) { + owner->bus.sendMulticast(); + return sys::MessageNone{}; + } return std::make_shared(simCard->handlePinUnlock(msg->pin)); }); @@ -93,6 +116,15 @@ namespace cellular::internal simCard->handleTrayState(); return sys::MessageNone{}; }); + owner->connect(typeid(cellular::SimInsertedNotication), [&](sys::Message *request) -> sys::MessagePointer { + auto message = static_cast(request); + + if (simCard->isSimSelectInProgress()) { + return sys::MessageNone{}; + } + simCard->handleSimInsertionNotification(message->getInsertedStatus()); + return sys::MessageNone{}; + }); /** * Internal message handlers diff --git a/module-services/service-cellular/src/SimCard.cpp b/module-services/service-cellular/src/SimCard.cpp index f106b7a977b2fcdced7df02e748eed393702aa45..24f3822bcc243b786e5775b6a84c41d478bd06f3 100644 --- a/module-services/service-cellular/src/SimCard.cpp +++ b/module-services/service-cellular/src/SimCard.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace cellular { @@ -63,6 +64,8 @@ namespace cellular Store::GSM::get()->selected = static_cast(sim); bsp::cellular::sim::simSelect(); bsp::cellular::sim::hotSwapTrigger(); + clearSimInsertedStatus(); + simSelectInProgress = true; return true; } @@ -302,5 +305,52 @@ namespace cellular return sim::Result::OK; } + + bool SimCard::isSimCardInserted() + { + if (simInserted == std::nullopt) { + if (simInserted = readSimCardInsertStatus(); !simInserted) { + return false; + } + } + + if (simInserted == at::SimInsertedStatus::Inserted || simInserted == at::SimInsertedStatus::Unknown) { + return true; + } + return false; + } + + std::optional SimCard::readSimCardInsertStatus() + { + auto command = at::cmd::QSIMSTAT(at::cmd::Modifier::Get); + auto response = channel->cmd(command); + auto result = command.parseQSIMSTAT(response); + + if (result.code != at::Result::Code::OK) { + LOG_ERROR("Can't read SIM insertion status."); + return std::nullopt; + } + return result.status; + } + void SimCard::handleSimTimer() + { + simSelectInProgress = false; + if (!isSimCardInserted()) { + if (onSimNotPresent) { + onSimNotPresent(); + } + } + } + void SimCard::handleSimInsertionNotification(at::SimInsertedStatus status) + { + if (auto actual = getSimInsertedStatus(); actual.has_value() && actual != status) { + setSimInserted(status); + if (status == at::SimInsertedStatus::Removed) { + if (onSimNotPresent) { + onSimNotPresent(); + } + } + } + } } // namespace service } // namespace cellular diff --git a/module-services/service-cellular/src/SimCard.hpp b/module-services/service-cellular/src/SimCard.hpp index 64f3e9536e360bd5571f0bf756863cb87d5e46c8..46cd2480823558d866a1edbec3d031faae3d7bc1 100644 --- a/module-services/service-cellular/src/SimCard.hpp +++ b/module-services/service-cellular/src/SimCard.hpp @@ -7,6 +7,8 @@ #include #include +#include + namespace at { class Cmd; @@ -98,6 +100,67 @@ namespace cellular::service */ void handleATSimStateChange(at::SimState state); + /** + * Check if sim card is present in slot + * @return true if sim card is present in slot + */ + bool isSimCardInserted(); + + /** + * Set new sim inserted status + * @param newStatus + */ + void setSimInserted(at::SimInsertedStatus newStatus) + { + simInserted = newStatus; + } + + /** + * Gets sim inserted status + * @return actual value of sim inserted status + */ + std::optional getSimInsertedStatus() + { + return simInserted; + } + + /** + * Clears sim inserted status + */ + void clearSimInsertedStatus() + { + simInserted = std::nullopt; + } + + /** + * Gets Sim Select progress state + * @return true if sim selecting is in progres + */ + bool isSimSelectInProgress() + { + return simSelectInProgress; + } + + /** + * Sets Sim Select progress state + * @param inProgress new progress state + */ + void setSimSelectInProgress(bool inProgress) + { + simSelectInProgress = inProgress; + } + + /** + * Sim timer event handler + */ + void handleSimTimer(); + + /** + * Sim Inserted notification handler + * @param status new Sim Inserted status + */ + void handleSimInsertionNotification(at::SimInsertedStatus status); + /** * Notification events */ @@ -107,6 +170,7 @@ namespace cellular::service std::function onSimBlocked; std::function onSimEvent; std::function onUnhandledCME; + std::function onSimNotPresent; private: /** SIM card initialization sequence @@ -168,8 +232,16 @@ namespace cellular::service void handleSimState(at::SimState state); + /** + * Read sim card insert status + * @return sim card inserted status + */ + std::optional readSimCardInsertStatus(); + at::BaseChannel *channel = nullptr; std::optional sim = std::nullopt; + std::optional simInserted = std::nullopt; + bool simSelectInProgress = false; }; } // namespace cellular::service diff --git a/module-services/service-fota/FotaUrcHandler.hpp b/module-services/service-fota/FotaUrcHandler.hpp index 1d96743adbc53bae9743b922f2a0babf4c2ef1cc..7c12bf569689f8072c3cd193d2fe0b826d9cf67a 100644 --- a/module-services/service-fota/FotaUrcHandler.hpp +++ b/module-services/service-fota/FotaUrcHandler.hpp @@ -28,6 +28,7 @@ class FotaUrcHandler : public at::urc::UrcHandler virtual void Handle(at::urc::Ring &urc){}; virtual void Handle(at::urc::PoweredDown &urc){}; virtual void Handle(at::urc::UrcResponse &urc){}; + virtual void Handle(at::urc::QSimstat &urc){}; private: FotaService::Service &fotaService; diff --git a/products/PurePhone/services/appmgr/ApplicationManager.cpp b/products/PurePhone/services/appmgr/ApplicationManager.cpp index 25cc677c0a80300eca79ea8261959c96440de55d..01d28139bc611120bcb98fbe0301f7aa69eb2a95 100644 --- a/products/PurePhone/services/appmgr/ApplicationManager.cpp +++ b/products/PurePhone/services/appmgr/ApplicationManager.cpp @@ -296,6 +296,9 @@ namespace app::manager } return simLockHandler.handleCMEErrorRequest(data->code); }); + connect(typeid(cellular::msg::notification::SimNotInserted), [&](sys::Message *request) -> sys::MessagePointer { + return simLockHandler.handleSimNotInsertedMessage(); + }); connect(typeid(locks::SetSim), [&](sys::Message *request) -> sys::MessagePointer { auto data = static_cast(request); simLockHandler.setSim(data->getSimSlot());