[plasma/plasma-nm] /: cellular: Overhaul roaming setting and state per SIM
Devin Lin <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 1edd68db8fa5b5f3e44dd77d6e4acf69f817936d by Devin Lin.
Committed on 25/07/2026 at 02:59.
Pushed by devinlin into branch 'master'.
cellular: Overhaul roaming setting and state per SIM
BUG: 515488
Overhaul the roaming setting in the cellular model (for Plasma Mobile), so that roaming states are tracked globally within a SIM. This allows for the currently set roaming state to transfer over when the connection changes.
M +3 -4 kcms/kcm_cellularnetwork/ui/SimPage.qml
M +23 -32 libs/cellular/cellularconnectionprofile.cpp
M +1 -3 libs/cellular/cellularconnectionprofile.h
M +92 -9 libs/cellular/cellularmodem.cpp
M +12 -2 libs/cellular/cellularmodem.h
https://invent.kde.org/plasma/plasma-nm/-/commit/1edd68db8fa5b5f3e44dd77d6e4acf69f817936d
diff --git a/kcms/kcm_cellularnetwork/ui/SimPage.qml b/kcms/kcm_cellularnetwork/ui/SimPage.qml
index 757b86b67..13947d7a8 100644
--- a/kcms/kcm_cellularnetwork/ui/SimPage.qml
+++ b/kcms/kcm_cellularnetwork/ui/SimPage.qml
@@ -60,14 +60,13 @@ FormCard.FormCardPage {
description: i18n("Allow mobile data when on a roaming network.")
property Cellular.CellularModem modem: sim ? sim.modem : null
- property int activeProfileIndex: modem ? modem.profiles.indexOfConnection(modem.activeConnectionUni) : -1
- property bool shouldBeChecked: activeProfileIndex >= 0 ? modem.profiles.roamingAllowedForConnection(modem.activeConnectionUni) : false
+ property bool shouldBeChecked: modem && modem.roamingAllowed
- enabled: modem && modem.mobileDataEnabled && activeProfileIndex >= 0
+ enabled: modem && modem.activeConnectionUni.length > 0 && !modem.profileOperationInProgress
checked: shouldBeChecked
onToggled: {
- modem.profiles.setRoamingAllowed(activeProfileIndex, checked);
+ modem.setRoamingAllowed(checked);
}
}
diff --git a/libs/cellular/cellularconnectionprofile.cpp b/libs/cellular/cellularconnectionprofile.cpp
index e82a167dc..d834fcb9b 100644
--- a/libs/cellular/cellularconnectionprofile.cpp
+++ b/libs/cellular/cellularconnectionprofile.cpp
@@ -107,52 +107,43 @@ void CellularConnectionProfile::refreshProfiles()
Q_EMIT profilesChanged();
}
-int CellularConnectionProfile::indexOfConnection(const QString &connectionUni) const
+QCoro::Task<bool> CellularConnectionProfile::setRoamingAllowed(QString connectionUni, bool allowed)
{
- for (int i = 0; i < m_list.count(); ++i) {
- if (m_list[i].connectionUni == connectionUni) {
- return i;
- }
- }
- return -1;
-}
-
-bool CellularConnectionProfile::roamingAllowedForConnection(const QString &connectionUni) const
-{
- int idx = indexOfConnection(connectionUni);
- if (idx < 0) {
- return false;
- }
- return m_list[idx].roamingAllowed;
-}
-
-QCoro::Task<void> CellularConnectionProfile::setRoamingAllowed(int index, bool allowed)
-{
- if (index < 0 || index >= m_list.count()) {
- co_return;
- }
-
- const QString &connectionUni = m_list[index].connectionUni;
NetworkManager::Connection::Ptr con = NetworkManager::findConnectionByUuid(connectionUni);
if (!con) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << u"Could not find connection"_s << connectionUni << u"to update roaming!"_s;
- co_return;
+ co_return false;
}
- NetworkManager::GsmSetting::Ptr gsmSetting = con->settings()->setting(NetworkManager::Setting::Gsm).dynamicCast<NetworkManager::GsmSetting>();
+ NetworkManager::ConnectionSettings::Ptr settings = con->settings();
+ NetworkManager::GsmSetting::Ptr gsmSetting = settings ? settings->setting(NetworkManager::Setting::Gsm).dynamicCast<NetworkManager::GsmSetting>() : nullptr;
if (!gsmSetting) {
- co_return;
+ co_return false;
+ }
+
+ const bool oldHomeOnly = gsmSetting->homeOnly();
+ if (oldHomeOnly == !allowed) {
+ co_return true;
}
gsmSetting->setHomeOnly(!allowed);
- QDBusReply<void> reply = co_await con->update(con->settings()->toMap());
+ QDBusReply<void> reply = co_await con->update(settings->toMap());
if (!reply.isValid()) {
+ gsmSetting->setHomeOnly(oldHomeOnly);
qCWarning(PLASMA_NM_CELLULAR_LOG) << u"Error updating roaming for"_s << connectionUni << u":"_s << reply.error().message();
- } else {
- m_list[index].roamingAllowed = allowed;
- Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {RoamingAllowed});
+ co_return false;
}
+
+ for (int index = 0; index < m_list.count(); ++index) {
+ if (m_list[index].connectionUni == connectionUni) {
+ m_list[index].roamingAllowed = allowed;
+ Q_EMIT dataChanged(createIndex(index, 0), createIndex(index, 0), {RoamingAllowed});
+ break;
+ }
+ }
+
+ co_return true;
}
QString CellularConnectionProfile::networkTypeStr(NetworkManager::GsmSetting::NetworkType networkType)
diff --git a/libs/cellular/cellularconnectionprofile.h b/libs/cellular/cellularconnectionprofile.h
index f2b07fb43..572bde34d 100644
--- a/libs/cellular/cellularconnectionprofile.h
+++ b/libs/cellular/cellularconnectionprofile.h
@@ -43,9 +43,7 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
- Q_INVOKABLE QCoro::Task<void> setRoamingAllowed(int index, bool allowed);
- Q_INVOKABLE int indexOfConnection(const QString &connectionUni) const;
- Q_INVOKABLE bool roamingAllowedForConnection(const QString &connectionUni) const;
+ QCoro::Task<bool> setRoamingAllowed(QString connectionUni, bool allowed);
void setNmModem(NetworkManager::ModemDevice::Ptr nmModem);
void refreshProfiles();
diff --git a/libs/cellular/cellularmodem.cpp b/libs/cellular/cellularmodem.cpp
index c83739e78..2485a313f 100644
--- a/libs/cellular/cellularmodem.cpp
+++ b/libs/cellular/cellularmodem.cpp
@@ -262,6 +262,22 @@ bool CellularModem::simEmpty() const
return !hasSim();
}
+bool CellularModem::roamingAllowed() const
+{
+ NetworkManager::Connection::Ptr connection = activeProfile();
+ if (!connection || !connection->settings()) {
+ return false;
+ }
+
+ NetworkManager::GsmSetting::Ptr gsmSetting = connection->settings()->setting(NetworkManager::Setting::Gsm).dynamicCast<NetworkManager::GsmSetting>();
+ return gsmSetting && !gsmSetting->homeOnly();
+}
+
+bool CellularModem::profileOperationInProgress() const
+{
+ return m_profileOperationInProgress;
+}
+
int CellularModem::signalStrength() const
{
return m_mmInterface ? m_mmInterface->signalQuality().signal : 0;
@@ -329,15 +345,41 @@ void CellularModem::refreshProfiles()
{
m_profiles->refreshProfiles();
Q_EMIT profilesChanged();
+ Q_EMIT roamingAllowedChanged();
+}
+
+NetworkManager::Connection::Ptr CellularModem::activeProfile() const
+{
+ const QString connectionUni = activeConnectionUni();
+ if (connectionUni.isEmpty()) {
+ return {};
+ }
+
+ return NetworkManager::findConnectionByUuid(connectionUni);
+}
+
+void CellularModem::setProfileOperationInProgress(bool inProgress)
+{
+ if (m_profileOperationInProgress == inProgress) {
+ return;
+ }
+
+ m_profileOperationInProgress = inProgress;
+ Q_EMIT profileOperationInProgressChanged();
}
-QCoro::Task<void> CellularModem::activateProfile(const QString &connectionUni)
+QCoro::Task<void> CellularModem::activateProfile(QString connectionUni)
{
if (!m_nmModem) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "Cannot activate profile since there is no NetworkManager modem";
co_return;
}
+ if (m_profileOperationInProgress) {
+ qCWarning(PLASMA_NM_CELLULAR_LOG) << "Operation already in progress";
+ co_return;
+ }
+
qCDebug(PLASMA_NM_CELLULAR_LOG) << "Activating profile on modem" << m_nmModem->uni() << "for connection" << connectionUni;
NetworkManager::Connection::Ptr con;
@@ -357,25 +399,62 @@ QCoro::Task<void> CellularModem::activateProfile(const QString &connectionUni)
co_return;
}
+ setProfileOperationInProgress(true);
+ const bool allowRoaming = roamingAllowed();
+ if (!(co_await m_profiles->setRoamingAllowed(con->uuid(), allowRoaming))) {
+ addError(i18n("Could not apply the roaming preference. Activating the access point with its existing setting."));
+ }
+
// despite the documentation saying otherwise, activateConnection seems to need the DBus path, not uuid of the connection
QDBusReply<QDBusObjectPath> reply = co_await NetworkManager::activateConnection(con->path(), m_nmModem->uni(), QString());
if (!reply.isValid()) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "Error activating connection:" << reply.error().message();
addError(i18n("Error activating connection: %1", reply.error().message()));
+ setProfileOperationInProgress(false);
co_return;
}
refreshProfiles();
Q_EMIT activeConnectionUniChanged();
+ setProfileOperationInProgress(false);
+}
+
+QCoro::Task<void> CellularModem::setRoamingAllowed(bool allowed)
+{
+ if (m_profileOperationInProgress) {
+ co_return;
+ }
+
+ setProfileOperationInProgress(true);
+ NetworkManager::Connection::Ptr connection = activeProfile();
+ if (!connection) {
+ addError(i18n("Cannot change roaming because no access point is available."));
+ Q_EMIT roamingAllowedChanged();
+ setProfileOperationInProgress(false);
+ co_return;
+ }
+
+ if (!(co_await m_profiles->setRoamingAllowed(connection->uuid(), allowed))) {
+ addError(i18n("Error applying roaming preference to the selected access point."));
+ Q_EMIT roamingAllowedChanged();
+ setProfileOperationInProgress(false);
+ co_return;
+ }
+
+ refreshProfiles();
+ setProfileOperationInProgress(false);
}
QCoro::Task<void> CellularModem::addProfile(QString name, QString apn, QString username, QString password, QString networkType)
{
- if (!m_nmModem) {
+ if (!m_nmModem || m_profileOperationInProgress) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "Cannot add profile since there is no NetworkManager modem";
co_return;
}
+ setProfileOperationInProgress(true);
+ const bool allowRoaming = roamingAllowed();
+
NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Gsm)};
settings->setId(name);
settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid());
@@ -388,7 +467,8 @@ QCoro::Task<void> CellularModem::addProfile(QString name, QString apn, QString u
gsmSetting->setPassword(password);
gsmSetting->setPasswordFlags(password.isEmpty() ? NetworkManager::Setting::NotRequired : NetworkManager::Setting::AgentOwned);
gsmSetting->setNetworkType(CellularConnectionProfile::networkTypeFlag(networkType));
- gsmSetting->setHomeOnly(true); // New profiles default to roaming disabled
+ // New profiles inherit the active profile's roaming preference.
+ gsmSetting->setHomeOnly(!allowRoaming);
gsmSetting->setInitialized(true);
@@ -402,7 +482,10 @@ QCoro::Task<void> CellularModem::addProfile(QString name, QString apn, QString u
addError(i18n("Error adding connection: %1", reply.error().message()));
} else {
qCDebug(PLASMA_NM_CELLULAR_LOG) << "Successfully added a new connection" << name << "with APN" << apn;
+ refreshProfiles();
}
+
+ setProfileOperationInProgress(false);
}
QCoro::Task<void> CellularModem::removeProfile(const QString &connectionUni)
@@ -442,7 +525,7 @@ QCoro::Task<void> CellularModem::updateProfile(QString connectionUni, QString na
gsmSetting->setPassword(password);
gsmSetting->setPasswordFlags(password.isEmpty() ? NetworkManager::Setting::NotRequired : NetworkManager::Setting::AgentOwned);
gsmSetting->setNetworkType(CellularConnectionProfile::networkTypeFlag(networkType));
- // Note: roaming is not set here, it's managed per-profile via CellularConnectionProfile::setRoamingAllowed()
+ // Note: roaming is not set here, it's managed per-profile
gsmSetting->setInitialized(true);
@@ -459,21 +542,21 @@ QCoro::Task<void> CellularModem::updateProfile(QString connectionUni, QString na
}
}
-void CellularModem::addDetectedProfileSettings()
+QCoro::Task<void> CellularModem::addDetectedProfileSettings()
{
if (!m_mmModem) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "ModemManager device missing, cannot detect profile settings";
- return;
+ co_return;
}
if (!hasSim() || !m_mmModem->sim()) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "No SIM found, cannot detect profile settings";
- return;
+ co_return;
}
if (!m_mm3gppDevice) {
qCWarning(PLASMA_NM_CELLULAR_LOG) << "3gpp object not found, cannot detect profile settings";
- return;
+ co_return;
}
bool found = false;
@@ -500,7 +583,7 @@ void CellularModem::addDetectedProfileSettings()
name += u" - "_s + apnInfo[u"name"_s].toString();
}
- addProfile(name, apn, apnInfo[u"username"_s].toString(), apnInfo[u"password"_s].toString(), u"4G/3G/2G"_s);
+ co_await addProfile(name, apn, apnInfo[u"username"_s].toString(), apnInfo[u"password"_s].toString(), u"4G/3G/2G"_s);
}
}
}
diff --git a/libs/cellular/cellularmodem.h b/libs/cellular/cellularmodem.h
index 2350ba604..da0c03cfc 100644
--- a/libs/cellular/cellularmodem.h
+++ b/libs/cellular/cellularmodem.h
@@ -48,6 +48,8 @@ class PLASMANM_CELLULAR_EXPORT CellularModem : public QObject
Q_PROPERTY(bool simLocked READ simLocked NOTIFY simLockedChanged)
Q_PROPERTY(bool simEmpty READ simEmpty NOTIFY simEmptyChanged)
Q_PROPERTY(QString activeConnectionUni READ activeConnectionUni NOTIFY activeConnectionUniChanged)
+ Q_PROPERTY(bool roamingAllowed READ roamingAllowed NOTIFY roamingAllowedChanged)
+ Q_PROPERTY(bool profileOperationInProgress READ profileOperationInProgress NOTIFY profileOperationInProgressChanged)
Q_PROPERTY(bool mobileDataEnabled READ mobileDataEnabled WRITE setMobileDataEnabled NOTIFY mobileDataEnabledChanged)
Q_PROPERTY(bool mobileDataSupported READ mobileDataSupported NOTIFY mobileDataSupportedChanged)
@@ -78,6 +80,8 @@ public:
bool hasSim() const;
bool simLocked() const;
bool simEmpty() const;
+ bool roamingAllowed() const;
+ bool profileOperationInProgress() const;
int signalStrength() const;
QString operatorName() const;
@@ -86,11 +90,12 @@ public:
Q_INVOKABLE void clearErrors();
// Connection profile management
- Q_INVOKABLE QCoro::Task<void> activateProfile(const QString &connectionUni);
+ Q_INVOKABLE QCoro::Task<void> activateProfile(QString connectionUni);
+ Q_INVOKABLE QCoro::Task<void> setRoamingAllowed(bool allowed);
Q_INVOKABLE QCoro::Task<void> addProfile(QString name, QString apn, QString username, QString password, QString networkType);
Q_INVOKABLE QCoro::Task<void> removeProfile(const QString &connectionUni);
Q_INVOKABLE QCoro::Task<void> updateProfile(QString connectionUni, QString name, QString apn, QString username, QString password, QString networkType);
- Q_INVOKABLE void addDetectedProfileSettings(); // detect modem connection settings (ex. apn) and add a new connection
+ Q_INVOKABLE QCoro::Task<void> addDetectedProfileSettings(); // detect modem connection settings (ex. apn) and add a new connection
QList<CellularSim *> sims();
@@ -102,6 +107,8 @@ public:
Q_SIGNALS:
void modemDetailsChanged();
void profilesChanged();
+ void roamingAllowedChanged();
+ void profileOperationInProgressChanged();
void uniChanged();
void displayIdChanged();
void activeConnectionUniChanged();
@@ -123,6 +130,8 @@ private:
void refreshSims();
void refreshProfiles();
void addError(const QString &message);
+ NetworkManager::Connection::Ptr activeProfile() const;
+ void setProfileOperationInProgress(bool inProgress);
CellularModemDetails *m_details = nullptr;
CellularConnectionProfile *m_profiles = nullptr;
@@ -134,4 +143,5 @@ private:
QList<CellularSim *> m_sims;
QStringList m_errors;
+ bool m_profileOperationInProgress = false;
};