~aleteoryx/muditaos

adb6a640857b297d3e67b035d5ccc813565236a2 — breichel 5 years ago 7f2b7b4
[EGD-4063] Handling PUK/PIN (#1014)

[EGD-4063] Added handling of PIN/PUK codes for SIM card objects.
SimCard class was added (supply pin and puk, change sim, get state, get sim lock infromation, get attempts counters)
URCCpin via URC handler was added
New message type to send data handled in URCCpin handler to ServiceCellular
M CMakeLists.txt => CMakeLists.txt +1 -1
@@ 21,7 21,7 @@ message("TARGET_COMPILE_DEFINITIONS: ${TARGET_COMPILE_OPTIONS}")
message("TARGET_LIBRARIES: ${TARGET_LIBRARIES}")
message("TARGET_LINKER_FLAGS: ${TARGET_LINKER_FLAGS}")

add_executable(${PROJECT_NAME} "")
add_executable(${PROJECT_NAME} "" )

if (NOT ${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
	set(ENABLE_TESTS ON)

M changelog.md => changelog.md +8 -0
@@ 1,5 1,13 @@
# MuditaOS changelog


## Added

* `[cellular]` Added SIM PIN/PUK handling
* `[cellular]` Added change PIN functionality
* `[cellular]` Added possibility of unlock SIM card (no PIN on start) functionality


## [0.46.1 2020-11-13]

## Added

M module-cellular/CMakeLists.txt => module-cellular/CMakeLists.txt +1 -0
@@ 30,6 30,7 @@ set(SOURCES
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCreg.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCmti.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcClip.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcCpin.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcPoweredDown.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcResponse.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/at/src/UrcFactory.cpp

M module-cellular/at/Commands.hpp => module-cellular/at/Commands.hpp +9 -1
@@ 119,6 119,10 @@ namespace at
        CUSD_SEND,
        SET_SMS_STORAGE,
        CPIN,
        GET_CPIN,
        QPINC, /// Get Pin/Puk attempts. For standard SIM facility (parameter) is "SC"
        CLCK,
        CPWD,
        ENABLE_TIME_ZONE_UPDATE,
        SET_TIME_ZONE_REPORTING,
        DISABLE_TIME_ZONE_UPDATE,


@@ 199,7 203,11 @@ namespace at
            {AT::CUSD_CLOSE_SESSION, {"AT+CUSD=2"}},
            {AT::CUSD_SEND, {"AT+CUSD=1,"}},
            {AT::SET_SMS_STORAGE, {"AT+CPMS=\"SM\",\"SM\",\"SM\"", 300}},
            {AT::CPIN, {"AT+CPIN="}},
            {AT::CPIN, {"AT+CPIN=", default_timeout}},
            {AT::GET_CPIN, {"AT+CPIN?", default_timeout}},
            {AT::QPINC, {"AT+QPINC=", default_timeout}},
            {AT::CLCK, {"AT+CLCK=", default_timeout}},
            {AT::CPWD, {"AT+CPWD=", default_timeout}},
            {AT::ENABLE_TIME_ZONE_UPDATE, {"AT+CTZU=3"}},
            {AT::SET_TIME_ZONE_REPORTING, {"AT+CTZR=2"}},
            {AT::DISABLE_TIME_ZONE_UPDATE, {"AT+CTZU=0"}},

M module-cellular/at/ErrorCode.hpp => module-cellular/at/ErrorCode.hpp +1 -1
@@ 30,7 30,7 @@ namespace at
        SIM_PIN_required              = 11,
        SIM_PUKRequired               = 12,
        SIMFailure                    = 13,
        SIMBusy                       = 14,
        SIMBusy                       = 14, /*!< could be returned on removed card and QPINC, PINC ret 10 */
        SIMWrong                      = 15,
        IncorrectPassword             = 16,
        SIM_PIN2Required              = 17,

A module-cellular/at/SimState.hpp => module-cellular/at/SimState.hpp +30 -0
@@ 0,0 1,30 @@
// 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
{
    /// SimState extends AT states based on CPIN (URC and AT Command)
    enum class SimState
    {
        Ready,
        NotReady,

        SimPin,
        SimPuk,
        SimPin2,
        SimPuk2,
        PhNetPin,
        PhNetPuk,
        PhNetSPin,
        PhNetSPuk,
        PhSpPin,
        PhSpPuk,
        PhCorpPin,
        PhCorpPuk,

        Locked, ///< In case of attempt counters equal zero
        Unknown
    };
} // namespace at

A module-cellular/at/UrcCpin.hpp => module-cellular/at/UrcCpin.hpp +65 -0
@@ 0,0 1,65 @@
// 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 "SimState.hpp"

namespace at::urc
{

    /// +CPIN: <state> - Indicate SIM card pin state
    class Cpin : public Urc
    {
        enum Tokens
        {
            State = 0
        };

      public:
        static constexpr auto cpin_ready     = "READY";     ///<  MT is not pending for any password
        static constexpr auto cpin_not_ready = "NOT READY"; ///< +CPIN urc, SIM not ready
        static constexpr auto cpin_sim_pin   = "SIM PIN";   ///< MT is waiting for SIM PIN to be given
        static constexpr auto cpin_sim_puk   = "SIM PUK";   ///< MT is waiting for SIM PUK to be given
        static constexpr auto cpin_sim_pin2  = "SIM PIN2";  ///< MT is waiting for SIM PIN2 to be given
        static constexpr auto cpin_sim_puk2  = "SIM PUK2";  ///<  MT is waiting for SIM PUK2 to be given
        static constexpr auto cpin_phnet_pin =
            "PH-NET PIN"; ///<  MT is waiting for network personalization password to be given
        static constexpr auto cpin_phnet_puk =
            "PH-NET PUK"; ///<  MT is waiting for network personalization unblocking password
        static constexpr auto cpin_phnestsub_pin =
            "PH-NETSUB PIN"; ///<  MT is waiting for network subset personalization password to be given
        static constexpr auto cpin_phnestsub_puk =
            "PH-NETSUB PUK"; ///<  MT is waiting for network subset personalization unblocking password to be given
        static constexpr auto cpin_phsp_pin =
            "PH-SP PIN"; ///<  MT is waiting for service provider personalization password to be given
        static constexpr auto cpin_phsp_puk =
            "PH-SP PUK"; ///<  MT is waiting for service provider personalization unblocking password to be given
        static constexpr auto cpin_phcorp_pin =
            "PH-CORP PIN"; ///<  MT is waiting for corporate personalization password to be given
        static constexpr auto cpin_phcorp_puk =
            "PH-CORP PUK"; ///<  MT is waiting for corporate personalization unblocking password to be given

        static constexpr auto head = "+CPIN";

        static bool isURC(const std::string uHead)
        {
            return uHead.find(Cpin::head) != std::string::npos;
        }

        using Urc::Urc;

        [[nodiscard]] auto isValid() const noexcept -> bool override;
        [[nodiscard]] auto getMessage() const noexcept -> std::optional<std::string>;
        [[nodiscard]] auto getState() const noexcept -> std::optional<at::SimState>;

        void Handle(UrcHandler &h) final
        {
            h.Handle(*this);
        }

        static at::SimState parseState(std::string_view state);
    };

} // namespace at::urc

M module-cellular/at/UrcHandler.hpp => module-cellular/at/UrcHandler.hpp +2 -0
@@ 11,6 11,7 @@ namespace at::urc
    class Cusd;
    class Ctze;
    class Qind;
    class Cpin;
    class PoweredDown;
    class UrcResponse;



@@ 23,6 24,7 @@ namespace at::urc
        virtual void Handle(Cusd &urc)        = 0;
        virtual void Handle(Ctze &urc)        = 0;
        virtual void Handle(Qind &urc)        = 0;
        virtual void Handle(Cpin &urc)        = 0;
        virtual void Handle(PoweredDown &urc) = 0;
        virtual void Handle(UrcResponse &urc) = 0;
    };

M module-cellular/at/response.cpp => module-cellular/at/response.cpp +40 -0
@@ 10,6 10,46 @@ namespace at
{
    namespace response
    {
        std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head)
        {
            if (resp.code == at::Result::Code::OK) {
                if (resp.response.size()) {
                    for (auto el : resp.response) {
                        if (el.compare(0, head.length(), head) == 0) {
                            auto body = el.substr(head.length());
                            return utils::split(body, ",");
                        }
                    }
                }
            }
            return std::nullopt;
        }

        bool parseQPINC(const at::Result &resp, qpinc::AttemptsCounters &ret)
        {
            /// parse only first result from QPINC
            const std::string_view AT_QPINC_SC = "+QPINC:";
            if (auto tokens = getTokensForATCommand(resp, AT_QPINC_SC); tokens) {
                constexpr int QPINC_TokensCount = 3;
                if ((*tokens).size() == QPINC_TokensCount) {
                    utils::toNumeric((*tokens)[1], ret.PinCounter);
                    utils::toNumeric((*tokens)[2], ret.PukCounter);
                    return true;
                }
            }
            return false;
        }

        bool parseCLCK(const at::Result &resp, int &ret)
        {
            const std::string_view AT_CLCK = "+CLCK:";
            if (auto tokens = getTokensForATCommand(resp, AT_CLCK); tokens) {
                if ((*tokens).size() != 0) {
                    return utils::toNumeric((*tokens)[0], ret);
                }
            }
            return false;
        }

        bool parseCSQ(std::string response, std::string &result)
        {

M module-cellular/at/response.hpp => module-cellular/at/response.hpp +13 -0
@@ 13,12 13,25 @@ namespace at
{
    namespace response
    {
        namespace qpinc
        {
            /// Structure that holds parsed information from AT+QPINC command
            struct AttemptsCounters
            {
                int PinCounter; /*!<  PIN attempts counter */
                int PukCounter; /*!<  PUK attempts counter */
            };
        } // namespace qpinc

        std::vector<std::string> tokenize(std::string &response, std::string separator = ",");
        std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head);
        bool parseCSQ(std::string response, std::string &result);
        bool parseCSQ(std::string cellularResponse, uint32_t &result);
        bool parseCREG(std::string &response, uint32_t &result);
        bool parseCREG(std::string &response, std::string &result);
        bool parseQNWINFO(std::string &response, std::string &result);
        bool parseQPINC(const at::Result &resp, qpinc::AttemptsCounters &ret);
        bool parseCLCK(const at::Result &resp, int &ret);

        namespace creg
        {

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

#include "UrcCpin.hpp"

using namespace at::urc;

at::SimState Cpin::parseState(std::string_view state)
{
    const std::map<std::string_view, SimState> cpin_convert_map = {
        {Cpin::cpin_ready, SimState::Ready},
        {Cpin::cpin_not_ready, SimState::NotReady},
        {Cpin::cpin_sim_pin, SimState::SimPin},
        {Cpin::cpin_sim_puk, SimState::SimPuk},
        {Cpin::cpin_sim_pin2, SimState::SimPin2},
        {Cpin::cpin_sim_puk2, SimState::SimPuk2},
        {Cpin::cpin_phnet_pin, SimState::PhNetPin},
        {Cpin::cpin_phnet_puk, SimState::PhNetPuk},
        {Cpin::cpin_phnestsub_pin, SimState::PhNetSPin},
        {Cpin::cpin_phnestsub_puk, SimState::PhNetSPuk},
        {Cpin::cpin_phsp_pin, SimState::PhSpPin},
        {Cpin::cpin_phsp_puk, SimState::PhSpPuk},
        {Cpin::cpin_phcorp_pin, SimState::PhCorpPin},
        {Cpin::cpin_phcorp_puk, SimState::PhCorpPuk},

    };

    auto it = cpin_convert_map.find(state);

    if (it != cpin_convert_map.end()) {
        return it->second;
    }
    return SimState::Unknown;
}

auto Cpin::isValid() const noexcept -> bool
{
    return tokens.size() == magic_enum::enum_count<Tokens>();
}

auto Cpin::getMessage() const noexcept -> std::optional<std::string>
{
    if (!isValid()) {
        return std::nullopt;
    }
    return tokens[Tokens::State];
}

auto Cpin::getState() const noexcept -> std::optional<at::SimState>
{
    if (!isValid()) {
        return std::nullopt;
    }
    auto msg = getMessage();
    LOG_DEBUG("CPIN State: %s", (*msg).c_str());
    return parseState(*msg);
}

M module-cellular/at/src/UrcFactory.cpp => module-cellular/at/src/UrcFactory.cpp +4 -1
@@ 11,7 11,7 @@
#include <UrcClip.hpp>
#include <UrcPoweredDown.hpp>
#include <UrcResponse.hpp>

#include <UrcCpin.hpp>
using namespace at::urc;

std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)


@@ 43,6 43,9 @@ std::unique_ptr<Urc> UrcFactory::Create(const std::string &urcMessage)
    else if (Clip::isURC(head)) {
        return std::make_unique<Clip>(body);
    }
    else if (Cpin::isURC(head)) {
        return std::make_unique<Cpin>(body);
    }
    else if (PoweredDown::isURC(head)) {
        return std::make_unique<PoweredDown>(body);
    }

M module-cellular/test/unittest_URC.cpp => module-cellular/test/unittest_URC.cpp +22 -0
@@ 16,9 16,11 @@
#include "UrcCreg.hpp"
#include "UrcCmti.hpp"
#include "UrcClip.hpp"
#include "UrcCpin.hpp"
#include "UrcPoweredDown.hpp"
#include "UrcResponse.hpp"
#include "UrcFactory.hpp"
#include "SimState.hpp"

template <typename urcType> static auto getURC(std::unique_ptr<at::urc::Urc> &urc) -> std::shared_ptr<urcType>
{


@@ 673,6 675,26 @@ TEST_CASE("+CLIP")
    }
}

TEST_CASE("+CPIN")
{
    SECTION("CPIN supported")
    {
        auto urc  = at::urc::UrcFactory::Create("+CPIN: SIM PIN");
        auto cpin = getURC<at::urc::Cpin>(urc);
        REQUIRE(cpin);
        REQUIRE(cpin->isValid());
        REQUIRE(cpin->getState() == at::SimState::SimPin);
        REQUIRE(cpin->getMessage() == "SIM PIN");
    }
    SECTION("CPIN not supported")
    {
        auto urc  = at::urc::UrcFactory::Create("+CPIN: \"Unknown\"");
        auto cpin = getURC<at::urc::Cpin>(urc);
        REQUIRE(cpin);
        REQUIRE(cpin->getState() == at::SimState::Unknown);
    }
}

TEST_CASE("POWERED DOWN")
{
    SECTION("POWERED DOWN valid")

M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +2 -1
@@ 1,4 1,4 @@
project(service-cellular)
project(service-cellular)
message( "${PROJECT_NAME}  ${CMAKE_CURRENT_LIST_DIR}" )

set(SOURCES


@@ 8,6 8,7 @@ set(SOURCES
    ServiceCellular.cpp
    SignalStrength.cpp
    CallRequest.cpp
    SimCard.cpp
    CallRequestFactory.cpp
    CellularCallRequestHandler.cpp
)

M module-services/service-cellular/CellularUrcHandler.cpp => module-services/service-cellular/CellularUrcHandler.cpp +14 -0
@@ 138,6 138,20 @@ void CellularUrcHandler::Handle(Qind &urc)
    }
}

void CellularUrcHandler::Handle(Cpin &urc)
{
    if (urc.isValid()) {
        auto state = urc.getState();
        if (!state) {
            LOG_INFO("Invalid cpin - ignore");
        }
        else {
            response = std::make_unique<CellularSimStateMessage>(*state, *urc.getMessage());
            urc.setHandled(true);
        }
    }
}

void CellularUrcHandler::Handle(PoweredDown &urc)
{
    if (urc.isValid()) {

M module-services/service-cellular/CellularUrcHandler.hpp => module-services/service-cellular/CellularUrcHandler.hpp +3 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 12,7 12,7 @@
#include <module-cellular/at/UrcCreg.hpp>
#include <module-cellular/at/UrcCtze.hpp>
#include <module-cellular/at/UrcCusd.hpp>
#include <module-cellular/at/UrcHandler.hpp>
#include <module-cellular/at/UrcCpin.hpp>
#include <module-cellular/at/UrcPoweredDown.hpp>
#include <module-cellular/at/UrcQind.hpp>
#include <module-cellular/at/UrcResponse.hpp>


@@ 34,6 34,7 @@ class CellularUrcHandler : public UrcHandler
    void Handle(Cusd &urc) final;
    void Handle(Ctze &urc) final;
    void Handle(Qind &urc) final;
    void Handle(Cpin &urc) final;
    void Handle(PoweredDown &urc) final;
    void Handle(UrcResponse &urc) final;


M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +191 -69
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CellularUrcHandler.hpp"


@@ 9,6 9,7 @@
#include "service-cellular/SignalStrength.hpp"
#include "service-cellular/State.hpp"
#include "service-cellular/USSD.hpp"
#include "SimCard.hpp"
#include "service-cellular/CallRequestFactory.hpp"
#include "service-cellular/CellularCallRequestHandler.hpp"



@@ 40,6 41,7 @@
#include <at/UrcCtze.hpp>
#include <at/UrcCusd.hpp>
#include <at/UrcQind.hpp>
#include <at/UrcCpin.hpp> // for Cpin
#include <at/response.hpp>
#include <bsp/cellular/bsp_cellular.hpp>
#include <common_data/EventStore.hpp>


@@ 646,47 648,6 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
        state.set(this, State::ST::SimSelect);
        break;
    }
    case MessageType::CellularSimVerifyPinRequest: {
        auto msg = dynamic_cast<CellularSimVerifyPinRequestMessage *>(msgl);
        if (msg != nullptr) {
            auto channel = cmux->get(TS0710::Channel::Commands);
            if (channel) {

                auto vpin = msg->gePinValue();

                std::stringstream ss;
                for (unsigned int i : vpin) {
                    ss << i;
                }
                std::string pin;
                ss >> pin;
                LOG_DEBUG("PIN SET %s", ss.str().c_str());

                sys::MessagePointer rmsg;

                if (Store::GSM::get()->selected == msg->getSimCard()) {

                    auto resp = channel->cmd(at::factory(at::AT::CPIN) + pin + ";\r");

                    if (resp.code == at::Result::Code::OK) {
                        responseMsg = std::make_shared<CellularResponseMessage>(true);
                    }
                    else {
                        responseMsg = std::make_shared<CellularResponseMessage>(false);
                        // TODO: Sim unlock error.
                    }
                }
                else {
                    LOG_ERROR("Selected SIM card differ from currently supported");
                    responseMsg = std::make_shared<CellularResponseMessage>(false);
                    at::Result fatalError;
                    fatalError.code = at::Result::Code::ERROR;
                    // TODO: Sim unlock error.
                }
            }
        }
        break;
    }
    case MessageType::CellularListCurrentCalls: {
        auto ret  = cmux->get(TS0710::Channel::Commands)->cmd(at::AT::CLCC);
        auto size = ret.response.size();


@@ 959,6 920,10 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
        auto resp   = transmitDtmfTone(msg->getDigit());
        responseMsg = std::make_shared<CellularResponseMessage>(resp);
    } break;
    case MessageType::CellularSimState: {
        auto msg    = static_cast<CellularSimStateMessage *>(msgl);
        responseMsg = std::make_shared<CellularResponseMessage>(handleSimState(msg->getState(), msg->getMessage()));
    } break;
    case MessageType::CellularUSSDRequest: {
        auto msg = dynamic_cast<CellularUSSDMessage *>(msgl);
        if (msg != nullptr) {


@@ 1005,8 970,14 @@ sys::MessagePointer ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl,
    }
}

/**
 * NOTICE: URC handling function identifyNotification works on different thread, so sending
 * any AT commands is not allowed here (also in URC handlers and other functions called from here)
 * @return
 */
std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotification(const std::string &data)
{

    CellularUrcHandler urcHandler(*this);

    std::string str(data.begin(), data.end());


@@ 1017,40 988,191 @@ std::optional<std::shared_ptr<CellularMessage>> ServiceCellular::identifyNotific
    auto urc = at::urc::UrcFactory::Create(str);
    urc->Handle(urcHandler);

    if (auto ret = str.find("+CPIN: ") != std::string::npos) {
        /// TODO handle different sim statuses - i.e. no sim, sim error, sim puk, sim pin etc.
        if (str.find("NOT READY", ret) == std::string::npos) {
    if (!urc->isHandled()) {
        LOG_WARN("Unhandled notification: %s", logStr.c_str());
    }

            Store::GSM::get()->sim = Store::GSM::get()->selected;
            if (str.find("SIM PIN", ret) != std::string::npos) {
                // TODO: Request pin.
            }
            else if (str.find("READY", ret) != std::string::npos) {
                // TODO: Sim unlocked.
            }
            else {
                LOG_WARN("Not supported: %s", logStr.c_str());
                Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
    return urcHandler.getResponse();
}

bool ServiceCellular::requestPin(unsigned int attempts, const std::string msg)
{
    LOG_DEBUG("REQUEST PIN");
    return true;
}

bool ServiceCellular::requestPuk(unsigned int attempts, const std::string msg)
{
    LOG_ERROR("REQUEST PUK");
    return true;
}

bool ServiceCellular::sendSimUnlocked()
{
    LOG_DEBUG("SIM UNLOCKED");
    return true;
}

bool ServiceCellular::sendSimBlocked()
{
    LOG_ERROR("SIM BLOCKED");
    return true;
}

bool ServiceCellular::sendUnhandledCME(unsigned int cme_error)
{
    LOG_ERROR("UNHANDLED CME %d", cme_error);
    return true;
}

bool ServiceCellular::sendBadPin()
{
    LOG_DEBUG("SEND BAD PIN");
    SimCard simCard(*this);
    std::string msg;
    if (auto state = simCard.simStateWithMessage(msg); state) {
        return handleSimState(*state, msg);
    }
    return false;
}

bool ServiceCellular::sendBadPuk()
{
    LOG_DEBUG("SEND BAD PUK");
    SimCard simCard(*this);
    std::string msg;
    if (auto state = simCard.simStateWithMessage(msg); state) {
        return handleSimState(*state, msg);
    }
    return false;
}

bool ServiceCellular::sendChangePinResult(SimCardResult res)
{
    LOG_DEBUG("SEND CHANGE PIN RESULT");
    return true;
}

bool ServiceCellular::changePin(const std::string oldPin, const std::string newPin)
{
    SimCard simCard(*this);
    sendChangePinResult(simCard.changePin(oldPin, newPin));
    return true;
}

bool ServiceCellular::unlockSimPin(std::string pin)
{
    SimCard simCard(*this);
    SimCardResult sime;
    LOG_DEBUG("PIN:  %s", pin.c_str());
    sime = simCard.supplyPin(pin);

    if (sime == SimCardResult::IncorrectPassword) {
        sendBadPin();
        return false;
    }

    if (sime == SimCardResult::OK) {
        return true;
    }
    else {
        sendUnhandledCME(static_cast<unsigned int>(sime));
        return false;
    }
}

bool ServiceCellular::unlockSimPuk(std::string puk, std::string pin)
{
    SimCard simCard(*this);
    SimCardResult sime;
    LOG_DEBUG("PUK:  %s  %s", puk.c_str(), pin.c_str());
    sime = simCard.supplyPuk(puk, pin);

    if (sime == SimCardResult::IncorrectPassword) {
        sendBadPuk();
        return false;
    }

    if (sime == SimCardResult::OK) {
        return true;
    }
    sendUnhandledCME(static_cast<unsigned int>(sime));
    return false;
}

bool ServiceCellular::handleSimState(at::SimState state, const std::string message)
{

    std::optional<std::unique_ptr<CellularMessage>> response;
    switch (state) {
    case at::SimState::Ready:
        Store::GSM::get()->sim = Store::GSM::get()->selected;
        // SIM causes SIM INIT, only on ready
        response = std::make_unique<CellularNotificationMessage>(CellularNotificationMessage::Type::SIM);
        sendSimUnlocked();
        break;
    case at::SimState::NotReady:
        LOG_DEBUG("Not ready");
        Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
        break;
    case at::SimState::SimPin: {
        SimCard simCard(*this);
        if (auto pc = simCard.getAttemptsCounters(); pc) {
            if (pc.value().PukCounter != 0) {
                requestPin(pc.value().PinCounter, message);
                break;
            }
            LOG_DEBUG("SIM OK!");
        }
        else {
            LOG_ERROR("SIM ERROR");
            Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
        }
        if ((str.find("NOT", ret) == std::string::npos) && (str.find("READY", ret) != std::string::npos)) {
            return std::make_shared<CellularNotificationMessage>(CellularNotificationMessage::Type::SIM);
        sendSimBlocked();
        break;
    }
    case at::SimState::SimPuk: {
        SimCard simCard(*this);
        if (auto pc = simCard.getAttemptsCounters(); pc) {
            if (pc.value().PukCounter != 0) {
                requestPuk(pc.value().PukCounter, message);
                break;
            }
        }
        auto message = std::make_shared<sevm::SIMMessage>();
        sys::Bus::SendUnicast(message, service::name::evt_manager, this);
        return std::nullopt;
        sendSimBlocked();
        break;
    }

    if (!urc->isHandled()) {
        LOG_WARN("Unhandled notification: %s", logStr.c_str());
    case at::SimState::SimPin2:
        [[fallthrough]];
    case at::SimState::SimPuk2:
        [[fallthrough]];
    case at::SimState::PhNetPin:
        [[fallthrough]];
    case at::SimState::PhNetPuk:
        [[fallthrough]];
    case at::SimState::PhNetSPin:
        [[fallthrough]];
    case at::SimState::PhNetSPuk:
        [[fallthrough]];
    case at::SimState::PhSpPin:
        [[fallthrough]];
    case at::SimState::PhSpPuk:
        [[fallthrough]];
    case at::SimState::PhCorpPin:
        [[fallthrough]];
    case at::SimState::PhCorpPuk:
        Store::GSM::get()->sim = Store::GSM::SIM::SIM_UNKNOWN;
        LOG_ERROR("SimState not supported");
        break;
    case at::SimState::Locked:
        Store::GSM::get()->sim = Store::GSM::SIM::SIM_FAIL;
        sendSimBlocked();
        break;
    case at::SimState::Unknown:
        LOG_ERROR("SimState not supported");
        Store::GSM::get()->sim = Store::GSM::SIM::SIM_UNKNOWN;
        break;
    }

    return urcHandler.getResponse();
    auto simMessage = std::make_shared<sevm::SIMMessage>();
    sys::Bus::SendUnicast(simMessage, service::name::evt_manager, this);

    return true;
}

bool ServiceCellular::sendSMS(SMSRecord record)

A module-services/service-cellular/SimCard.cpp => module-services/service-cellular/SimCard.cpp +169 -0
@@ 0,0 1,169 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SimCard.hpp"
#include <log/log.hpp>
#include <variant>
#include "Result.hpp"
#include "UrcCpin.hpp" //for Cpin parseState
#include "UrcFactory.hpp"

SimCardResult SimCard::convertErrorFromATResult(const at::Result atres) const
{
    if (std::holds_alternative<at::EquipmentErrorCode>(atres.errorCode)) {

        auto cerr = static_cast<int>(std::get<at::EquipmentErrorCode>(atres.errorCode));
        if ((cerr > static_cast<int>(SimCardResult::AT_ERROR_Begin)) &&
            (cerr < static_cast<int>(SimCardResult::AT_ERROR_End))) {
            return static_cast<SimCardResult>(cerr);
        }
    }
    return SimCardResult::Unknown;
}

std::optional<at::response::qpinc::AttemptsCounters> SimCard::getAttemptsCounters() const
{
    auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
    if (channel) {
        auto resp = channel->cmd(at::factory(at::AT::QPINC) + "\"SC\"");
        at::response::qpinc::AttemptsCounters ret;
        if (at::response::parseQPINC(resp, ret)) {
            return ret;
        }
    }

    return std::nullopt;
}

SimCardResult SimCard::supplyPin(const std::string pin) const
{
    if (auto pc = getAttemptsCounters(); pc) {
        if (pc.value().PinCounter > 0) {
            if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
                auto resp = channel->cmd(at::factory(at::AT::CPIN) + "\"" + pin + "\"");

                if (resp.code == at::Result::Code::OK) {
                    return SimCardResult::OK;
                }
                else {
                    return convertErrorFromATResult(resp);
                }
            }
        }
        else {
            if (pc.value().PukCounter > 0) {
                return SimCardResult::SIM_PUKRequired;
            }
            else {
                return SimCardResult::Locked;
            }
        }
    }
    return SimCardResult::Unknown;
}

SimCardResult SimCard::supplyPuk(const std::string puk, const std::string pin) const
{
    if (auto pc = getAttemptsCounters(); pc) {
        if (pc.value().PukCounter != 0) {
            if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
                auto resp = channel->cmd(at::factory(at::AT::CPIN) + "\"" + puk + "\"" + ",\"" + pin + "\"");
                if (resp.code == at::Result::Code::OK) {
                    return SimCardResult::OK;
                }
                else {
                    return convertErrorFromATResult(resp);
                }
            }
        }
        else {
            return SimCardResult::Locked;
        }
    }

    return SimCardResult::Unknown;
}

bool SimCard::isPinLocked() const
{
    if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
        auto resp = channel->cmd(at::factory(at::AT::CLCK) + "\"SC\",2\r");
        int val   = 0;
        if (at::response::parseCLCK(resp, val)) {
            return val != 0;
        }
    }
    return true;
}

SimCardResult SimCard::setPinLock(bool lock, const std::string pin) const
{
    if (auto pc = getAttemptsCounters(); pc) {
        if (pc.value().PukCounter != 0) {
            auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
            if (channel) {
                auto resp =
                    channel->cmd(at::factory(at::AT::CLCK) + "\"SC\"," + (lock ? "1" : "0") + ",\"" + pin + "\"");
                if (resp.code == at::Result::Code::OK) {
                    return SimCardResult::OK;
                }
                else {
                    return convertErrorFromATResult(resp);
                }
            }
        }
        else {
            return SimCardResult::Locked;
        }
    }

    return SimCardResult::Unknown;
}

SimCardResult SimCard::changePin(const std::string oldPin, const std::string newPin) const
{
    if (auto pc = getAttemptsCounters(); pc) {
        if (pc.value().PukCounter != 0) {
            auto channel = cellularService.cmux->get(TS0710::Channel::Commands);
            if (channel) {
                auto resp = channel->cmd(at::factory(at::AT::CPWD) + "\"SC\", \"" + oldPin + "\",\"" + newPin + "\"");
                if (resp.code == at::Result::Code::OK) {
                    return SimCardResult::OK;
                }
                else {
                    return convertErrorFromATResult(resp);
                }
            }
        }
        else {
            return SimCardResult::Locked;
        }
    }

    return SimCardResult::Unknown;
}

std::optional<at::SimState> SimCard::simState() const
{
    std::string buf;
    return simStateWithMessage(buf);
}

std::optional<at::SimState> SimCard::simStateWithMessage(std::string &message) const
{
    if (auto channel = cellularService.cmux->get(TS0710::Channel::Commands); channel) {
        auto resp = channel->cmd(at::factory(at::AT::GET_CPIN));
        if (resp.code == at::Result::Code::OK) {
            if (resp.response.size()) {
                for (auto el : resp.response) {
                    auto urc  = at::urc::UrcFactory::Create(el);
                    auto cpin = std::unique_ptr<at::urc::Cpin>{static_cast<at::urc::Cpin *>(urc.release())};
                    if (cpin) {
                        return cpin->getState();
                    }
                }
            }
        }
    }
    return at::SimState::Unknown;
}

A module-services/service-cellular/SimCard.hpp => module-services/service-cellular/SimCard.hpp +76 -0
@@ 0,0 1,76 @@
// 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 <string>

#include <SimState.hpp>

#include "service-cellular/SimCardResult.hpp"
#include "service-cellular/CellularMessage.hpp"
#include "service-cellular/CellularServiceAPI.hpp"
#include "service-cellular/ServiceCellular.hpp"
#include "response.hpp"

class SimCard
{
  public:
    explicit SimCard(ServiceCellular &cellularService) : cellularService(cellularService)
    {}

    /** Get information about attempts of PIN and PUK for standard sim card (eg. not PIN2)
     * @return  As optional SimCard::AttemptsCounters, in case of error nullopt. Should be noted that in some case could
     * return SIMFailure which could mean 0 attempts (happen if lock during session, on modem/sim reboot again return
     * 0,0);
     */
    std::optional<at::response::qpinc::AttemptsCounters> getAttemptsCounters() const;

    /** Supply pin for modem
     * \param pin digits as a string from 4-8 digits
     * \return return OK on success in other case see details in SimCardResult
     */
    SimCardResult supplyPin(const std::string pin) const;

    /** Supply pin for modem
     * \param puk puk as standard 8 digits
     * \param pin, new pin digits as a string from 4-8 digits
     * \return return OK on success in other case see details in SimCardResult
     */
    SimCardResult supplyPuk(const std::string puk, const std::string pin) const;

    /** return whether the pin needs to be provided, only for standard pin.
     * \return true if need pin to unlock SIM card functionality
     */
    bool isPinLocked() const;

    /** Set whether to provide pin. Always need to provide actual pin for sim card, only for standard PIN
     * \param lock true for lock SIM card
     * \param pin actual pin for SIM card
     * \return
     */
    SimCardResult setPinLock(bool lock, const std::string pin) const;

    /** Change pin, only for standard pin. To get effect of change pin, SIM cart or modem should be restarted
     * simplest solution is to call AT+CFUN=0/1
     * \param oldPin
     * \param newPin
     * \return return OK on success, else see SimCardResult
     */
    SimCardResult changePin(const std::string oldPin, const std::string newPin) const;

    /** Return SIM state based on CPIN AT commands
     */
    std::optional<at::SimState> simState() const;
    std::optional<at::SimState> simStateWithMessage(std::string &message) const;

  private:
    ServiceCellular &cellularService;

    /** Helper function to convert from one enum to another (only with part of them). Base on continuous range of sim
     * errors in AT.
     */
    SimCardResult convertErrorFromATResult(const at::Result) const;

    std::optional<std::vector<std::string>> getTokensForATCommand(const at::Result &resp, std::string_view head);
};

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +37 -1
@@ 12,6 12,7 @@
#include <Service/Message.hpp>
#include <module-bsp/bsp/cellular/bsp_cellular.hpp>
#include <utf8/UTF8.hpp>
#include <SimState.hpp>

#include <memory>
#include <string>


@@ 79,6 80,29 @@ class CellularNotificationMessage : public CellularMessage
    Type type;
    std::string data;
};

class CellularSimStateMessage : public CellularMessage
{
  private:
    at::SimState state;
    std::string message;

  public:
    explicit CellularSimStateMessage(at::SimState state, std::string message)
        : CellularMessage(MessageType::CellularSimState), state(state), message(std::move(message))
    {}

    at::SimState getState() const noexcept
    {
        return state;
    }

    std::string getMessage() const
    {
        return message;
    }
};

class CellularTimeNotificationMessage : public CellularMessage
{
  private:


@@ 241,20 265,32 @@ class CellularSimResponseMessage : public CellularSimMessage
    static const SimState defaultSimState         = SimState::SIMUnlocked;
};

/// Message use only for mockup GUI purposes
class CellularSimVerifyPinRequestMessage : public CellularSimMessage
{
  public:
    CellularSimVerifyPinRequestMessage(Store::GSM::SIM sim, std::vector<unsigned int> pinValue)
        : CellularSimMessage(MessageType::CellularSimVerifyPinRequest, sim), pinValue(std::move(pinValue))
    {}
    CellularSimVerifyPinRequestMessage(Store::GSM::SIM sim,
                                       std::vector<unsigned int> pinValue,
                                       std::vector<unsigned int> pukValue)
        : CellularSimMessage(MessageType::CellularSimVerifyPinRequest, sim), pinValue(std::move(pinValue)),
          pukValue(std::move(pukValue))
    {}

    std::vector<unsigned int> gePinValue() const noexcept
    std::vector<unsigned int> getPinValue() const
    {
        return pinValue;
    }
    std::vector<unsigned int> getPukValue() const
    {
        return pukValue;
    }

  private:
    std::vector<unsigned int> pinValue;
    std::vector<unsigned int> pukValue;
};

class CellularGetChannelMessage : public sys::DataMessage

M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +72 -1
@@ 1,8 1,10 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// 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 "SimCardResult.hpp"

#include "CellularCall.hpp"
#include "CellularMessage.hpp"
#include "State.hpp"


@@ 29,6 31,7 @@
#include <vector>

class MuxDaemon;

namespace db
{
    namespace query


@@ 81,8 84,72 @@ class ServiceCellular : public sys::Service
    std::vector<std::string> getNetworkInfo();
    std::vector<std::string> scanOperators();

    /** group of action/messages send "outside" eg. GUI
     * requestPin is call anytime modem need pin, here should be called any action
     * which allow user input (or mockup) pin. Then send appropriate action to notify the modem
     * \param attempts Attempts counter for current action
     * \param msg Literal name of action eg. SIM PIN
     * \return
     */
    bool requestPin(unsigned int attempts, const std::string msg);

    /** requestPuk is call anytime modem need puk, here should be called any action
     * which allow user input (or mockup) puk and new pin. Then send appropriate action to notify the modem
     * \param attempts Attempts counter for current action
     * \param msg Literal name of action eg. SIM PUK
     * \return
     */
    bool requestPuk(unsigned int attempts, const std::string msg);

    /** Call in case of SIM card unlocked, MT ready. Place for sending message/action inform rest
     * \return
     */
    bool sendSimUnlocked();

    /** Call in case of SIM card locked (card fail, eg. to many bad PUK). Place for sending message/action inform rest
     * \return
     */
    bool sendSimBlocked();

    /** From this point should be send message/action call interaction in other layers eg. GUI
     * \param cme_error
     * \return
     */
    bool sendUnhandledCME(unsigned int cme_error);

    /** Similar to sendBadPin
     * \return
     */
    bool sendBadPin();

    /** Message send, when modem return incorrect password for PIN message.
     * Probably modem firmware depend. On current version last bad message (attempts=1) return PUK request
     * and generate PUK URC, so finally action on puk request will be call. This implementation allow to
     * rethrow URC (so achive similar behavior in all cases).
     * \return
     */
    bool sendBadPuk();

    /** Place to send action notifying eg. GUI
     * \param res
     * \return
     */
    bool sendChangePinResult(SimCardResult res);

    /// sim functionality

    /** Function ready for change pin action send to Service Cellular form eg. GUI
     * \param oldPin
     * \param newPin
     * \return
     */
    bool changePin(const std::string oldPin, const std::string newPin);
    bool unlockSimPin(std::string pin);
    bool unlockSimPuk(std::string puk, std::string pin);

  private:
    std::unique_ptr<TS0710> cmux = std::make_unique<TS0710>(PortSpeed_e::PS460800, this);

    // used for polling for call state
    std::unique_ptr<sys::Timer> callStateTimer;
    std::unique_ptr<sys::Timer> stateTimer;


@@ 151,6 218,7 @@ class ServiceCellular : public sys::Service
    /// \note some run state should be added to ignore non system messages now...
    bool handle_fatal_failure();
    bool handle_ready();

    bool handleAllMessagesFromMessageStorage();
    [[nodiscard]] SMSRecord createSMSRecord(const UTF8 &decodedMessage,
                                            const UTF8 &receivedNumber,


@@ 182,6 250,9 @@ class ServiceCellular : public sys::Service
    bool handleUSSDURC();
    void handleUSSDTimer();

    bool handleSimState(at::SimState state, const std::string message);

    friend class CellularUrcHandler;
    friend class CellularCallRequestHandler;
    friend class SimCard;
};

A module-services/service-cellular/service-cellular/SimCardResult.hpp => module-services/service-cellular/service-cellular/SimCardResult.hpp +28 -0
@@ 0,0 1,28 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

enum class SimCardResult
{
    OK     = 0,
    Ready  = 1,
    Locked = 2, /*!< In case of attempt counters set to 0 */

    AT_ERROR_Begin =
        9, /*!< this is only for separate AT SIM errors from new one added, AT errors list end with AT_ERROR_End */

    SIMNotInserted    = 10,
    SIM_PIN_required  = 11,
    SIM_PUKRequired   = 12,
    Failure           = 13,
    Busy              = 14,
    Wrong             = 15,
    IncorrectPassword = 16,

    AT_ERROR_End = 17,

    Unknown = 0xFF /*!< Unknown, any reason (not only AT), in some case AT commends return just error, eg. twice
                              supply good pin, second AT commend return ERROR */

};

M module-services/service-fota/FotaUrcHandler.hpp => module-services/service-fota/FotaUrcHandler.hpp +1 -0
@@ 25,6 25,7 @@ class FotaUrcHandler : public UrcHandler
    virtual void Handle(Cmti &urc){};
    virtual void Handle(Cusd &urc){};
    virtual void Handle(Ctze &urc){};
    virtual void Handle(Cpin &urc){};
    virtual void Handle(PoweredDown &urc){};
    virtual void Handle(UrcResponse &urc){};


M source/MessageType.hpp => source/MessageType.hpp +1 -0
@@ 101,6 101,7 @@ enum class MessageType
    CellularTransmitDtmfTones,
    CellularUSSDRequest,
    CellularTimeUpdated,
    CellularSimState,

    DBNotesAdd,            ///< Add new note's record
    DBNotesRemove,         ///< Remove selected note's record