~aleteoryx/muditaos

e7444ff39ff87f729bf32595ecd7eaa11f6d5182 — Bartosz Cichocki 3 years ago 09556d4
[MOS-263] Fix DTMF handling in Pure

By accident, I've broke DTMF handling via Pure's keyboard
This was because of non strict description of API method.
Now, it has been unified to use ASCII code
M module-apps/application-call/model/CallModel.cpp => module-apps/application-call/model/CallModel.cpp +7 -2
@@ 85,9 85,14 @@ namespace app::call
        return succeed;
    }

    void CallModel::transmitDtmfTone(const uint32_t &digit)
    void CallModel::transmitDtmfTone(const uint8_t &digitCode)
    {
        CellularServiceAPI::TransmitDtmfTones(application, digit);
        try {
            CellularServiceAPI::TransmitDtmfTones(application, DTMFCode(static_cast<char>(digitCode)));
        }
        catch (std::out_of_range &e) {
            LOG_ERROR("Can't send DTMF code for digit: %c", static_cast<char>(digitCode));
        }
    }

    utils::PhoneNumber CallModel::getPhoneNumber()

M module-apps/application-call/model/CallModel.hpp => module-apps/application-call/model/CallModel.hpp +2 -2
@@ 59,7 59,7 @@ namespace app::call
        virtual void hangUpCall()                            = 0;
        virtual void answerCall()                            = 0;
        virtual bool sendSms(const UTF8 &smsBody)            = 0;
        virtual void transmitDtmfTone(const uint32_t &digit) = 0;
        virtual void transmitDtmfTone(const uint8_t &digitCode) = 0;
        virtual void muteCall()                              = 0;
        virtual void unmuteCall()                            = 0;
        virtual void turnLoudspeakerOn()                     = 0;


@@ 89,7 89,7 @@ namespace app::call
        void hangUpCall() final;
        void answerCall() final;
        bool sendSms(const UTF8 &smsBody) final;
        void transmitDtmfTone(const uint32_t &digit) final;
        void transmitDtmfTone(const uint8_t &digitCode) final;
        void muteCall();
        void unmuteCall();
        void turnLoudspeakerOn();

M module-apps/application-call/test/mock/CallPresenterMocks.hpp => module-apps/application-call/test/mock/CallPresenterMocks.hpp +1 -1
@@ 98,7 98,7 @@ namespace app::call
        {
            return true;
        }
        void transmitDtmfTone(const uint32_t &digit) override{};
        void transmitDtmfTone(const uint8_t &digitCode) override{};
        void muteCall() override{};
        void unmuteCall() override{};
        void turnLoudspeakerOn() override{};

M module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp +6 -1
@@ 319,7 319,12 @@ namespace bluetooth
        case HFP_SUBEVENT_TRANSMIT_DTMF_CODES: {
            auto digitStr = hfp_subevent_transmit_dtmf_codes_get_dtmf(event);
            LOG_DEBUG("Send DTMF Codes: '%s'\n", digitStr);
            cellularInterface->sendDTMFCode(const_cast<sys::Service *>(ownerService), utils::toNumeric(digitStr));
            try {
                cellularInterface->sendDTMFCode(const_cast<sys::Service *>(ownerService), DTMFCode(digitStr));
            }
            catch (std::out_of_range &e) {
                LOG_ERROR("Can't send DTMF code for digit: %s", digitStr);
            }
            hfp_ag_send_dtmf_code_done(aclHandle);
        } break;
        case HFP_SUBEVENT_CALL_ANSWERED:

M module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.cpp => module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.cpp +2 -2
@@ 16,9 16,9 @@ namespace bluetooth
    {
        return CellularServiceAPI::HangupCall(service);
    }
    bool CellularInterfaceImpl::sendDTMFCode(sys::Service *service, uint32_t digit)
    bool CellularInterfaceImpl::sendDTMFCode(sys::Service *service, DTMFCode code)
    {
        auto msg = std::make_shared<CellularDtmfRequestMessage>(digit);
        auto msg = std::make_shared<CellularDtmfRequestMessage>(code);
        service->bus.sendUnicast(std::move(msg), service::name::cellular);
        return true;
    }

M module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.hpp => module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.hpp +3 -2
@@ 4,6 4,7 @@
#pragma once

#include <Service/ServiceForward.hpp>
#include <module-services/service-cellular/DTMFCode.hpp>

namespace bluetooth
{


@@ 13,7 14,7 @@ namespace bluetooth
        virtual ~CellularInterface()                           = default;
        virtual bool answerIncomingCall(sys::Service *service) = 0;
        virtual bool hangupCall(sys::Service *service)         = 0;
        virtual bool sendDTMFCode(sys::Service *service, uint32_t digit)          = 0;
        virtual bool sendDTMFCode(sys::Service *service, DTMFCode code)           = 0;
        virtual bool dialNumber(sys::Service *service, const std::string &number) = 0;
    };



@@ 23,7 24,7 @@ namespace bluetooth
        bool answerIncomingCall(sys::Service *service) override;
        bool hangupCall(sys::Service *service) override;
        bool dialNumber(sys::Service *service, const std::string &number) override;
        bool sendDTMFCode(sys::Service *service, uint32_t digit) override;
        bool sendDTMFCode(sys::Service *service, DTMFCode code) override;
    };

    class AudioInterface

M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +1 -0
@@ 14,6 14,7 @@ set(SOURCES
    src/ModemResetHandler.cpp
    src/URCCounter.cpp
    src/CSQHandler.cpp
        DTMFCode.cpp

    CellularServiceAPI.cpp
    CellularUrcHandler.cpp

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

#include "service-cellular/CellularMessage.hpp"


@@ 258,9 258,9 @@ bool CellularServiceAPI::GetAntenna(sys::Service *serv, bsp::cellular::antenna &
    return false;
}

bool CellularServiceAPI::TransmitDtmfTones(sys::Service *serv, uint32_t digit)
bool CellularServiceAPI::TransmitDtmfTones(sys::Service *serv, DTMFCode code)
{
    auto msg = std::make_shared<CellularDtmfRequestMessage>(digit);
    auto msg = std::make_shared<CellularDtmfRequestMessage>(code);
    return serv->bus.sendUnicast(msg, ServiceCellular::serviceName);
}


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

#include "DTMFCode.hpp"
#include <Utils.hpp>
#include <stdexcept>

DTMFCode::DTMFCode(const char digitChar)
{
    if ((digitChar >= '0' && digitChar <= '9') || digitChar == '*' || digitChar == '#') {
        this->digitChar = digitChar;
    }
    else {
        this->digitChar = 0;
        throw std::out_of_range("DTMF: Invalid number!");
    }
}
DTMFCode::DTMFCode(const char *digitStr)
{
    auto digitTmpString = std::string{digitStr};
    if (digitTmpString == "*" || digitTmpString == "#") {
        digitChar = digitTmpString[0];
    }
    else {
        try {
            auto digit = std::stoi(digitTmpString);
            if (digit > 9) {
                throw std::out_of_range("DTMF: Invalid number!");
            }
            digitChar = digit + '0';
        }
        catch (std::invalid_argument &e) {
            LOG_ERROR("Can't parse digit string to DTMF ASCII code");
            digitChar = 0;
            throw std::out_of_range("DTMF: Invalid number!");
        }
    }
}
auto DTMFCode::getDigitASCIICode() const -> char
{
    return digitChar;
}
DTMFCode::operator std::string() const
{
    return "\"" + std::string(1, getDigitASCIICode()) + "\"";
}

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

#pragma once
#include <stdint.h>
#include <string>

class DTMFCode
{
  public:
    /// \brief Creates a DTMFCode instance via passing a digit character (its ASCII code)
    /// \param digitChar - digit's ASCII code
    explicit DTMFCode(char digitChar);

    /// \brief Creates a DTMFCode instance via passing a digit C-string
    /// \param digitChar - C-string containing code's digit
    explicit DTMFCode(const char *digitString);

    [[nodiscard]] auto getDigitASCIICode() const -> char;
    operator std::string() const;

  private:
    char digitChar;
};

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +4 -5
@@ 1468,17 1468,16 @@ std::string ServiceCellular::GetScanMode(void)
    return {};
}

bool ServiceCellular::transmitDtmfTone(uint32_t digit)
bool ServiceCellular::transmitDtmfTone(DTMFCode code)
{
    auto channel = cmux->get(CellularMux::Channel::Commands);
    at::Result resp;
    if (channel) {
        auto command           = at::factory(at::AT::QLDTMF);
        std::string dtmfString = "\"" + utils::singleDigitToString(digit) + "\"";
        resp                   = channel->cmd(command.getCmd() + dtmfString);
        resp                   = channel->cmd(command.getCmd() + std::string(code));
        if (resp) {
            command = at::factory(at::AT::VTS);
            resp    = channel->cmd(command.getCmd() + dtmfString);
            resp    = channel->cmd(command.getCmd() + std::string(code));
        }
    }
    return resp.code == at::Result::Code::OK;


@@ 2105,7 2104,7 @@ auto ServiceCellular::handleCellularGetAntennaMessage(sys::Message *msg) -> std:
auto ServiceCellular::handleCellularDtmfRequestMessage(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>
{
    auto message = static_cast<CellularDtmfRequestMessage *>(msg);
    auto resp    = transmitDtmfTone(message->getDigit());
    auto resp    = transmitDtmfTone(message->getDTMFCode());
    return std::make_shared<CellularResponseMessage>(resp);
}
auto ServiceCellular::handleCellularUSSDMessage(sys::Message *msg) -> std::shared_ptr<sys::ResponseMessage>

M module-services/service-cellular/service-cellular/CellularMessage.hpp => module-services/service-cellular/service-cellular/CellularMessage.hpp +5 -4
@@ 25,6 25,7 @@
#include <service-appmgr/data/CallActionsParams.hpp>

#include <service-cellular/api/common.hpp>
#include <DTMFCode.hpp>

class CellularMessage : public sys::DataMessage
{


@@ 307,15 308,15 @@ class CellularRequestMessage : public CellularMessage

class CellularDtmfRequestMessage : public CellularMessage
{
    uint32_t digit = 0;
    DTMFCode code;

  public:
    CellularDtmfRequestMessage(uint32_t digit) : CellularMessage(Type::TransmitDtmfTones), digit(digit)
    CellularDtmfRequestMessage(DTMFCode code) : CellularMessage(Type::TransmitDtmfTones), code(code)
    {}

    uint32_t getDigit() const
    DTMFCode getDTMFCode() const
    {
        return digit;
        return code;
    }
};


M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp => module-services/service-cellular/service-cellular/CellularServiceAPI.hpp +7 -2
@@ 13,6 13,7 @@

#include <cstdint>
#include <string>
#include <module-services/service-cellular/DTMFCode.hpp>

class Service;
namespace sys


@@ 88,8 89,12 @@ namespace CellularServiceAPI
    bool GetCREG(sys::Service *serv, std::string &response);
    bool GetQNWINFO(sys::Service *serv, std::string &response);
    bool GetAntenna(sys::Service *serv, bsp::cellular::antenna &response);

    bool TransmitDtmfTones(sys::Service *serv, uint32_t digit);
    /**
     * @brief Transmits DTMF tone
     * @param serv
     * @param code - DTMF code to be sent
     */
    bool TransmitDtmfTones(sys::Service *serv, DTMFCode code);

    bool USSDRequest(sys::Service *serv, CellularUSSDMessage::RequestType type, std::string data = "");


M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +2 -1
@@ 27,6 27,7 @@
#include <PhoneModes/Observer.hpp>
#include <service-db/DBServiceName.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <DTMFCode.hpp>

#include <optional> // for optional
#include <memory>   // for unique_ptr, allocator, make_unique, shared_ptr


@@ 203,7 204,7 @@ class ServiceCellular : public sys::Service
    [[nodiscard]] bool receiveAllMessages();
    /// @}

    bool transmitDtmfTone(uint32_t digit);
    bool transmitDtmfTone(DTMFCode digit);
    /// Handle message CellularGetChannelMessage
    void handle_CellularGetChannelMessage();


M module-services/service-cellular/tests/CMakeLists.txt => module-services/service-cellular/tests/CMakeLists.txt +8 -0
@@ 75,3 75,11 @@ add_catch2_executable(
        LIBS
        module-cellular
)
add_catch2_executable(
        NAME
        DTMFCode
        SRCS
        unittest_DTMFCode.cpp
        LIBS
        module-cellular
)

A module-services/service-cellular/tests/unittest_DTMFCode.cpp => module-services/service-cellular/tests/unittest_DTMFCode.cpp +79 -0
@@ 0,0 1,79 @@
// 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 "DTMFCode.hpp"

TEST_CASE("DTMFCode")
{
    SECTION("Parse from char")
    {
        auto code = DTMFCode('0');
        REQUIRE(code.getDigitASCIICode() == '0');
        REQUIRE(std::string(code) == "\"0\"");
    }
    SECTION("Parse from char 2")
    {
        auto code = DTMFCode('9');
        REQUIRE(code.getDigitASCIICode() == '9');
        REQUIRE(std::string(code) == "\"9\"");
    }
    SECTION("Parse from char 3")
    {
        auto code = DTMFCode('*');
        REQUIRE(code.getDigitASCIICode() == '*');
        REQUIRE(std::string(code) == "\"*\"");
    }
    SECTION("Parse from char 4")
    {
        auto code = DTMFCode('#');
        REQUIRE(code.getDigitASCIICode() == '#');
        REQUIRE(std::string(code) == "\"#\"");
    }

    SECTION("Parse from string")
    {
        auto code = DTMFCode("0");
        REQUIRE(code.getDigitASCIICode() == '0');
        REQUIRE(std::string(code) == "\"0\"");
    }
    SECTION("Parse from string 2")
    {
        auto code = DTMFCode("9");
        REQUIRE(code.getDigitASCIICode() == '9');
        REQUIRE(std::string(code) == "\"9\"");
    }
    SECTION("Parse from string 3")
    {
        auto code = DTMFCode("*");
        REQUIRE(code.getDigitASCIICode() == '*');
        REQUIRE(std::string(code) == "\"*\"");
    }
    SECTION("Parse from string 4")
    {
        auto code = DTMFCode("#");
        REQUIRE(code.getDigitASCIICode() == '#');
        REQUIRE(std::string(code) == "\"#\"");
    }

    SECTION("Parse from char - incorrect input")
    {
        REQUIRE_THROWS_AS(DTMFCode('a'), std::out_of_range);
    }
    SECTION("Parse from char - incorrect input 2")
    {
        REQUIRE_THROWS_AS(DTMFCode('!'), std::out_of_range);
    }
    SECTION("Parse from string - incorrect input 1")
    {
        REQUIRE_THROWS_AS(DTMFCode("a"), std::out_of_range);
    }
    SECTION("Parse from string - incorrect input 2")
    {
        REQUIRE_THROWS_AS(DTMFCode("!"), std::out_of_range);
    }
    SECTION("Parse from string - incorrect input 3")
    {
        REQUIRE_THROWS_AS(DTMFCode("12"), std::out_of_range);
    }
}

M module-utils/utility/Utils.hpp => module-utils/utility/Utils.hpp +0 -20
@@ 23,26 23,6 @@ namespace utils
    std::string bytesToHex(const std::vector<std::uint8_t> &bytes);
    std::vector<std::uint8_t> hexToBytes(const std::string &hex);

    template <typename T> inline char singleDigitToChar(T digit)
    {
        static_assert(std::is_integral<T>::value, "Integral number required.");

        if (digit > 9 || digit < 0) {
            return '\0';
        }
        // converting digit to ASCII code
        return static_cast<char>(digit + '0');
    }

    template <typename T> inline std::string singleDigitToString(T digit)
    {
        auto charDigit = singleDigitToChar(digit);
        if (charDigit == '\0') {
            return std::string();
        }
        return std::string(1, charDigit);
    }

    template <typename T> std::string numToHex(T c)
    {
        std::stringstream s;

M module-utils/utility/tests/unittest_utils.cpp => module-utils/utility/tests/unittest_utils.cpp +0 -35
@@ 446,38 446,3 @@ TEST_CASE("Generate random Id")
        REQUIRE((ret.size() == expectedSize));
    }
}

TEST_CASE("singleDigitToString test")
{

    SECTION("proper input")
    {
        uint8_t digit = 5;
        auto str      = utils::singleDigitToString(digit);
        REQUIRE(str == "5");
    }
    SECTION("proper input 2")
    {
        int digit = 9;
        auto str  = utils::singleDigitToString(digit);
        REQUIRE(str == "9");
    }
    SECTION("improper input - not a single digit")
    {
        int digit = 15;
        auto str  = utils::singleDigitToString(digit);
        REQUIRE(str.empty());
    }
    SECTION("improper input - negative number")
    {
        int digit = -10;
        auto str  = utils::singleDigitToString(digit);
        REQUIRE(str.empty());
    }
    SECTION("improper input - not an integer-related type")
    {
        char digit = '6';
        auto str   = utils::singleDigitToString(digit);
        REQUIRE(str.empty());
    }
}