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