~aleteoryx/muditaos

acfec6c3dbbfb35e5b0f25787f315f3c16e5e309 — breichel 5 years ago f3625a8
[EGD-5273] Add possibility of add new and delete APN

Functionality based on existing functions for APN.
additionally, context number management
(base on Quectel correspondence)
and saving APN to settings has been added.
M module-services/service-cellular/CellularServiceAPI.cpp => module-services/service-cellular/CellularServiceAPI.cpp +13 -0
@@ 330,6 330,19 @@ bool CellularServiceAPI::SetAPN(sys::Service *serv, packet_data::APN::Config apn
    return sys::Bus::SendUnicast(std::make_shared<CellularSetAPNMessage>(apn), ServiceCellular::serviceName, serv);
}

bool CellularServiceAPI::NewAPN(sys::Service *serv, packet_data::APN::Config apnConfig)
{
    auto apn = std::make_shared<packet_data::APN::Config>(std::move(apnConfig));
    return sys::Bus::SendUnicast(std::make_shared<CellularNewAPNMessage>(apn), ServiceCellular::serviceName, serv);
}

bool CellularServiceAPI::DeleteAPN(sys::Service *serv, std::uint8_t contextId)
{
    auto emptyApn       = std::make_shared<packet_data::APN::Config>();
    emptyApn->contextId = contextId;
    return sys::Bus::SendUnicast(std::make_shared<CellularSetAPNMessage>(emptyApn), ServiceCellular::serviceName, serv);
}

bool CellularServiceAPI::SetDataTransfer(sys::Service *serv, packet_data::DataTransfer dt)
{
    return sys::Bus::SendUnicast(

M module-services/service-cellular/PacketData.cpp => module-services/service-cellular/PacketData.cpp +85 -38
@@ 177,23 177,37 @@ namespace packet_data

    PacketData::PacketData(ServiceCellular &cellularService) : cellularService(cellularService){};

    void PacketData::loadAPNSettings()
    void PacketData::loadAPNSettings(const std::string &json)
    {

        std::shared_ptr<APN::Config> apnConfig = std::make_shared<APN::Config>();
        apnConfig->contextId                   = 1;
        apnConfig->apnType                     = APN::APNType::Default;
        apnConfig->apn                         = "internet";
        apnConfig->authMethod                  = APN::AuthMethod::NONE;
        std::string err;
        auto apnsObj = json11::Json::parse(json, err).array_items();
        if (!err.empty()) {
            LOG_ERROR("Error while parsing APN configuration: %s", err.c_str());
            return;
        }

        contextMap[apnConfig->contextId] = apnConfig;
        contextMap.clear();
        for (auto &it : apnsObj) {
            std::shared_ptr<APN::Config> apnConfig = std::make_shared<APN::Config>();
            apnConfig->from_json(it);
            contextMap[apnConfig->contextId] = apnConfig;
        }

        LOG_ERROR("loadAPNSettings");
        LOG_DEBUG("loadAPNSettings");
    }

    void PacketData::saveAPNSettings()
    std::string PacketData::saveAPNSettings() const
    {
        /// Save in phone memory
        /// Save as JSON string
        std::vector<json11::Json> vec;

        std::transform(contextMap.begin(), contextMap.end(), std::back_inserter(vec), [](auto &apn) {
            return apn.second->to_json();
        });
        LOG_DEBUG("saveAPNSettings");

        return json11::Json(vec).dump();
    }

    at::Result::Code PacketData::updateAPNSettings(std::uint8_t ctxId)


@@ 202,44 216,47 @@ namespace packet_data
        PDPContext pdpCtx(cellularService);
        std::shared_ptr<APN::Config> apnConfig;
        std::shared_ptr<APN::Config> modemApn;
        if ((modemApn = pdpCtx.getConfiguration(ctxId)) && (modemApn != nullptr)) {

            auto phoneApn = contextMap.find(ctxId);
        if (!((modemApn = pdpCtx.getConfiguration(ctxId)) && (modemApn != nullptr))) {
            return at::Result::Code::ERROR;
        }
        auto phoneApn = contextMap.find(ctxId);

            if (phoneApn != contextMap.end()) {
                LOG_DEBUG("Phone context exists");
                if (dataTransfer == DataTransfer::Off) {
                    /// set null configuration, solution based on lack of quectel documentation
                    return pdpCtx.setConfiguration(phoneApn->second, true);
                }
                else {
                    if (!phoneApn->second->compare(modemApn)) {
                        /// rebuild configuratio
                        LOG_DEBUG("Update modem context %d", ctxId);
        if (phoneApn != contextMap.end()) {
            LOG_DEBUG("Phone context exists");
            if (dataTransfer == DataTransfer::Off) {
                /// set null configuration, solution based on lack of quectel documentation
                return pdpCtx.setConfiguration(phoneApn->second, true);
            }
            else {
                if (!phoneApn->second->compare(modemApn)) {
                    /// rebuild configuration
                    LOG_DEBUG("Update modem context %d", ctxId);
                    if (ctxId > internalAPNMaxId) {
                        return pdpCtx.setConfiguration(phoneApn->second);
                    }
                    else {
                        contextMap[ctxId] = modemApn;
                    }
                }
            }
            else {
                LOG_ERROR("Phone context not exists");
                if (!modemApn->isEmpty()) {
                    /** update phone configuration base on modem conf (eg. ims)
                     *
                     * Comment from quectel (2020.12):
                     * As I know, only VZW MBN would use IMS on CID 1 to register IMS service directly.
                     * So we usually ask customers to set up data connection on CID3 for VZW sim card.
                     * Regarding other MBN files, the CID 1 shouldn’t be IMS because the CID1 is used
                     * to activate default bearer for LTE network. So we usually ask customers to
                     * configure their own APN on CID1.  With this rule, it’s easy for customer
                     * to configure their APN no matter which MBN file is activated
                     */
                    LOG_ERROR("Update modem context %d", ctxId);
        }
        else {
            LOG_DEBUG("Phone context not exists");
            if (!modemApn->isEmpty()) {
                /** update phone configuration base on modem conf (eg. ims)
                 */
                LOG_DEBUG("Update modem context %d", ctxId);
                if (ctxId > internalAPNMaxId) {
                    return pdpCtx.setConfiguration(modemApn, true);
                }
                else {
                    contextMap[ctxId] = modemApn;
                }
            }
        }
        return at::Result::Code::OK;
    }

    void PacketData::setupAPNSettings()
    {
        for (std::uint8_t ctxId = MINContextId; ctxId <= MAXContextId; ctxId++) {


@@ 284,10 301,40 @@ namespace packet_data

    at::Result::Code PacketData::setAPN(std::shared_ptr<APN::Config> apn)
    {
        contextMap[apn->contextId] = apn;
        /// Only one default, save on phone configuration (not modem)
        if (apn->apnType == packet_data::APN::APNType::Default) {
            for (std::uint8_t ctxId = internalAPNMaxId + 1; ctxId <= MAXContextId; ctxId++) {

                auto phoneApn = contextMap.find(ctxId);
                if ((phoneApn != contextMap.end()) && (!phoneApn->second->isEmpty()) &&
                    (phoneApn->second->apnType == packet_data::APN::APNType::Default) && (ctxId != apn->contextId)) {
                    contextMap[ctxId]->apnType = packet_data::APN::APNType::Internet;
                }
            }
        }

        if (apn->isEmpty()) {
            contextMap.erase(apn->contextId);
        }
        else {
            contextMap[apn->contextId] = apn;
        }
        return updateAPNSettings(apn->contextId);
    }

    at::Result::Code PacketData::newAPN(std::shared_ptr<APN::Config> apn, std::uint8_t &newId)
    {
        for (std::uint8_t ctxId = internalAPNMaxId + 1; ctxId <= MAXContextId; ctxId++) {
            if (contextMap.find(ctxId) == contextMap.end()) {
                newId          = ctxId;
                apn->contextId = ctxId;
                return setAPN(apn);
            }
        }

        return at::Result::Code::ERROR;
    }

    bool PacketData::setDataTransfer(DataTransfer dt)
    {
        dataTransfer = dt;

M module-services/service-cellular/PacketData.hpp => module-services/service-cellular/PacketData.hpp +5 -4
@@ 59,18 59,18 @@ namespace packet_data
      public:
        explicit PacketData(ServiceCellular &cellularService);
        /**
         * @brief load APN settings from phone memory, not call modem func.
         * @brief load APN settings from JSON string, not call modem func.
         *
         * To synchronize with modem, this function should be call as soon as
         * the modem is ready (in sense of AT commands). Then should be call
         * setupAPNSettings to synchronize modem database with phone database.
         */
        void loadAPNSettings();
        void loadAPNSettings(const std::string &json);

        /**
         * @brief save all APN's in phone memory
         * @brief save all APN's JSON string
         */
        void saveAPNSettings();
        std::string saveAPNSettings() const;

        /**
         * @brief setup APN on modem, based on configuration loaded in loadAPNSettings


@@ 99,6 99,7 @@ namespace packet_data
         */
        std::optional<std::shared_ptr<APN::Config>> getAPNFirst(APN::APNType type);
        at::Result::Code setAPN(std::shared_ptr<APN::Config> apn);
        at::Result::Code newAPN(std::shared_ptr<APN::Config> apn, std::uint8_t &newId);
    };

} // namespace packet_data

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +41 -4
@@ 203,15 203,17 @@ ServiceCellular::ServiceCellular() : sys::Service(serviceName, "", cellularStack

        sys::Bus::SendMulticast(msg.value(), sys::BusChannels::ServiceCellularNotifications, this);
    };

    packetData = std::make_unique<packet_data::PacketData>(*this); /// call in apnListChanged handler

    registerMessageHandlers();
    packetData = std::make_unique<packet_data::PacketData>(*this);
    packetData->loadAPNSettings();
}

ServiceCellular::~ServiceCellular()
{
    LOG_INFO("[ServiceCellular] Cleaning resources");
    settings->unregisterValueChange(settings::Cellular::volte_on, ::settings::SettingsScope::Global);
    settings->unregisterValueChange(settings::Cellular::apn_list, ::settings::SettingsScope::Global);
}

// this static function will be replaced by Settings API


@@ 237,6 239,11 @@ sys::ReturnCodes ServiceCellular::InitHandler()
        settings::Cellular::volte_on,
        [this](const std::string &value) { volteChanged(value); },
        ::settings::SettingsScope::Global);
    settings->registerValueChange(
        settings::Cellular::apn_list,
        [this](const std::string &value) { apnListChanged(value); },
        ::settings::SettingsScope::Global);

    return sys::ReturnCodes::Success;
}



@@ 309,6 316,16 @@ void ServiceCellular::registerMessageHandlers()
        return handleCellularGetAPNMessage(msg);
    });

    connect(typeid(CellularSetAPNMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularSetAPNMessage *>(request);
        return handleCellularSetAPNMessage(msg);
    });

    connect(typeid(CellularNewAPNMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularNewAPNMessage *>(request);
        return handleCellularNewAPNMessage(msg);
    });

    connect(typeid(CellularSetDataTransferMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg = static_cast<CellularSetDataTransferMessage *>(request);
        return handleCellularSetDataTransferMessage(msg);


@@ 2195,17 2212,28 @@ std::shared_ptr<CellularGetAPNResponse> ServiceCellular::handleCellularGetAPNMes
}
std::shared_ptr<CellularSetAPNResponse> ServiceCellular::handleCellularSetAPNMessage(CellularSetAPNMessage *msg)
{

    auto apn = msg->getAPNConfig();
    auto ret = packetData->setAPN(apn);
    settings->setValue(settings::Cellular::apn_list, packetData->saveAPNSettings(), settings::SettingsScope::Global);
    return std::make_shared<CellularSetAPNResponse>(ret);
}

    return std::make_shared<CellularSetAPNResponse>(packetData->setAPN(apn));
std::shared_ptr<CellularNewAPNResponse> ServiceCellular::handleCellularNewAPNMessage(CellularNewAPNMessage *msg)
{
    auto apn           = msg->getAPNConfig();
    std::uint8_t newId = 0;
    auto ret           = packetData->newAPN(apn, newId);
    settings->setValue(settings::Cellular::apn_list, packetData->saveAPNSettings(), settings::SettingsScope::Global);
    return std::make_shared<CellularNewAPNResponse>(ret, newId);
}

std::shared_ptr<CellularSetDataTransferResponse> ServiceCellular::handleCellularSetDataTransferMessage(
    CellularSetDataTransferMessage *msg)
{
    packetData->setDataTransfer(msg->getDataTransfer());
    return std::make_shared<CellularSetDataTransferResponse>(at::Result::Code::OK);
}

std::shared_ptr<CellularGetDataTransferResponse> ServiceCellular::handleCellularGetDataTransferMessage(
    CellularGetDataTransferMessage *msg)
{


@@ 2218,6 2246,7 @@ std::shared_ptr<CellularActivateContextResponse> ServiceCellular::handleCellular
    return std::make_shared<CellularActivateContextResponse>(packetData->activateContext(msg->getContextId()),
                                                             msg->getContextId());
}

std::shared_ptr<CellularDeactivateContextResponse> ServiceCellular::handleCellularDeactivateContextMessage(
    CellularDeactivateContextMessage *msg)
{


@@ 2255,3 2284,11 @@ void ServiceCellular::volteChanged(const std::string &value)
        volteOn = utils::getNumericValue<bool>(value);
    }
}

void ServiceCellular::apnListChanged(const std::string &value)
{
    LOG_ERROR("apnListChanged");
    if (!value.empty()) {
        packetData->loadAPNSettings(value);
    }
}

M module-services/service-cellular/service-cellular/CellularServiceAPI.hpp => module-services/service-cellular/service-cellular/CellularServiceAPI.hpp +24 -0
@@ 107,7 107,31 @@ namespace CellularServiceAPI
     */
    bool GetAPN(sys::Service *serv, packet_data::APN::APNType type);

    /**
     * @brief set up/change existing APN.
     *
     * Allow to change internal APN, for creating New (not empty) APN
     * should be used NewAPN functionality (to not overwrite internal APN)
     *
     */
    bool SetAPN(sys::Service *serv, packet_data::APN::Config apnConfig);

    /**
     * @brief setup new APN, assigns a new contextId
     * @param serv
     * @param apnConfig
     * @return
     */
    bool NewAPN(sys::Service *serv, packet_data::APN::Config apnConfig);

    /**
     * @brief Call SetAPN with empty APN (delete it)
     * @param serv
     * @param contextId
     * @return
     */
    bool DeleteAPN(sys::Service *serv, std::uint8_t contextId);

    bool SetDataTransfer(sys::Service *serv, packet_data::DataTransfer dt);
    bool GetDataTransfer(sys::Service *serv);
    bool SetVoLTE(sys::Service *serv, bool value);

M module-services/service-cellular/service-cellular/PacketDataCellularMessage.hpp => module-services/service-cellular/service-cellular/PacketDataCellularMessage.hpp +30 -1
@@ 41,7 41,22 @@ class CellularSetAPNMessage : public CellularMessage // also as DeleteAPN

  public:
    CellularSetAPNMessage(std::shared_ptr<packet_data::APN::Config> apnConfig)
        : CellularMessage(MessageType::CellularPacketData), apnConfig(apnConfig)
        : CellularMessage(MessageType::CellularPacketData), apnConfig(std::move(apnConfig))
    {}

    [[nodiscard]] std::shared_ptr<packet_data::APN::Config> getAPNConfig() const noexcept
    {
        return apnConfig;
    }
};

class CellularNewAPNMessage : public CellularMessage
{
    std::shared_ptr<packet_data::APN::Config> apnConfig;

  public:
    CellularNewAPNMessage(std::shared_ptr<packet_data::APN::Config> apnConfig)
        : CellularMessage(MessageType::CellularPacketData), apnConfig(std::move(apnConfig))
    {}

    [[nodiscard]] std::shared_ptr<packet_data::APN::Config> getAPNConfig() const noexcept


@@ 142,6 157,20 @@ class CellularSetAPNResponse : public CellularATResponse
    {}
};

class CellularNewAPNResponse : public CellularATResponse
{
    std::uint8_t contextId;

  public:
    CellularNewAPNResponse(at::Result::Code result, std::uint8_t contextId)
        : CellularATResponse(result), contextId(contextId)
    {}
    [[nodiscard]] std::uint8_t getContextId() const noexcept
    {
        return contextId;
    }
};

class CellularSetDataTransferResponse : public CellularATResponse
{
  public:

M module-services/service-cellular/service-cellular/PacketDataTypes.hpp => module-services/service-cellular/service-cellular/PacketDataTypes.hpp +56 -6
@@ 10,9 10,25 @@

#include <Utils.hpp>

#include <json/json11.hpp>

namespace packet_data
{

    namespace json::APN
    {
        namespace Config
        {
            constexpr inline auto contextId   = "contextId";
            constexpr inline auto apnType     = "apnType";
            constexpr inline auto contextType = "contextType";
            constexpr inline auto authMethod  = "authMethod";
            constexpr inline auto apn         = "apn";
            constexpr inline auto username    = "username";
            constexpr inline auto password    = "password";

        } // namespace Config
    }     // namespace json::APN
    enum class DataTransfer
    {
        Off,


@@ 26,11 42,13 @@ namespace packet_data
         */
        enum class APNType
        {
            Default, ///< only one APN is set as default
            IMS,     ///< IP Multimedia Subsystem for eg VoLTE
            MMS,     ///< for MMS service
            Fota,    ///< for Firmware Update
            Internet //< for data traffic
            Default,  ///< only one APN is set as default
            IMS,      ///< IP Multimedia Subsystem for eg VoLTE
            MMS,      ///< for MMS service
            Fota,     ///< for Firmware Update
            Internet, ///< for internet connection, Default have same meaning (but default). If this change to default,
                      ///< existing Default will change to Internet
            Internal  ///< for Internal MBN APNs
        };

        /**


@@ 77,7 95,7 @@ namespace packet_data
          public:
            unsigned char contextId =
                0; /// context on which apn is configured available values 1-16, 0 - means not set yet
            APNType apnType         = APNType::Default;
            APNType apnType         = APNType::Internal;
            ContextState state      = ContextState::Deactivated;
            ContextType contextType = ContextType::ipv4; /// IP type
            AuthMethod authMethod   = AuthMethod::NONE;


@@ 167,12 185,44 @@ namespace packet_data
            {
                return !(c1 == c2);
            }

            void from_json(json11::Json json)
            {
                contextId = json[json::APN::Config::contextId].int_value();
                apnType   = static_cast<packet_data::APN::APNType>(json[json::APN::Config::apnType].int_value());
                contextType =
                    static_cast<packet_data::APN::ContextType>(json[json::APN::Config::contextType].int_value());
                authMethod = static_cast<packet_data::APN::AuthMethod>(json[json::APN::Config::authMethod].int_value());
                apn        = json[json::APN::Config::apn].string_value();
                username   = json[json::APN::Config::username].string_value();
                password   = json[json::APN::Config::password].string_value();
            }
            [[nodiscard]] auto to_json() const -> json11::Json
            {
                return json11::Json::object{{json::APN::Config::contextId, contextId},
                                            {json::APN::Config::apnType, static_cast<int>(apnType)},
                                            {json::APN::Config::contextType, static_cast<int>(contextType)},
                                            {json::APN::Config::authMethod, static_cast<int>(authMethod)},
                                            {json::APN::Config::apn, apn},
                                            {json::APN::Config::username, username},
                                            {json::APN::Config::password, password}};
            }
        };

    } // namespace APN

    constexpr unsigned char MINContextId = 1;
    constexpr unsigned char MAXContextId = 16;

    /** Comment from quectel (2020.12):
     * As I know, only VZW MBN would use IMS on CID 1 to register IMS service directly.
     * So we usually ask customers to set up data connection on CID3 for VZW sim card.
     * Regarding other MBN files, the CID 1 shouldn’t be IMS because the CID1 is used
     * to activate default bearer for LTE network. So we usually ask customers to
     * configure their own APN on CID1.  With this rule, it’s easy for customer
     * to configure their APN no matter which MBN file is activated
     */
    constexpr auto internalAPNMaxId      = 4;
    using ContextMap                     = std::unordered_map<unsigned char, std::shared_ptr<APN::Config>>;
    using ContextPair                    = std::pair<unsigned char, std::shared_ptr<APN::Config>>;


M module-services/service-cellular/service-cellular/ServiceCellular.hpp => module-services/service-cellular/service-cellular/ServiceCellular.hpp +2 -0
@@ 287,6 287,7 @@ class ServiceCellular : public sys::Service
        CellularGetCurrentOperatorMessage *msg);
    std::shared_ptr<CellularGetAPNResponse> handleCellularGetAPNMessage(CellularGetAPNMessage *msg);
    std::shared_ptr<CellularSetAPNResponse> handleCellularSetAPNMessage(CellularSetAPNMessage *msg);
    std::shared_ptr<CellularNewAPNResponse> handleCellularNewAPNMessage(CellularNewAPNMessage *msg);
    std::shared_ptr<CellularSetOperatorResponse> handleCellularSetOperator(CellularSetOperatorMessage *msg);
    std::shared_ptr<CellularSetDataTransferResponse> handleCellularSetDataTransferMessage(
        CellularSetDataTransferMessage *msg);


@@ 306,5 307,6 @@ class ServiceCellular : public sys::Service
    friend class packet_data::PacketData;

    void volteChanged(const std::string &value);
    void apnListChanged(const std::string &value);
    bool volteOn = false;
};

M module-services/service-db/agents/settings/SystemSettings.hpp => module-services/service-db/agents/settings/SystemSettings.hpp +1 -0
@@ 35,6 35,7 @@ namespace settings
    namespace Cellular
    {
        constexpr inline auto volte_on = "cl_volte_on";
        constexpr inline auto apn_list = "cl_apn_list";
    } // namespace Cellular

}; // namespace settings