File indexing completed on 2024-09-01 05:15:40
0001 // SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de> 0002 // SPDX-FileCopyrightText: 2022-2023 Devin Lin <devin@kde.org> 0003 // SPDX-License-Identifier: GPL-2.0-or-later 0004 0005 #include "signalindicator.h" 0006 0007 #include <NetworkManagerQt/GsmSetting> 0008 #include <NetworkManagerQt/Manager> 0009 #include <NetworkManagerQt/Settings> 0010 #include <NetworkManagerQt/Utils> 0011 0012 #include <KUser> 0013 0014 #include "signalindicator.h" 0015 0016 SignalIndicator::SignalIndicator(QObject *parent) 0017 : QObject{parent} 0018 , m_nmModem{nullptr} 0019 , m_modemDevice{nullptr} 0020 , m_modem{nullptr} 0021 , m_3gppModem{nullptr} 0022 { 0023 connect(ModemManager::notifier(), &ModemManager::Notifier::modemAdded, this, &SignalIndicator::updateModemManagerModem); 0024 connect(ModemManager::notifier(), &ModemManager::Notifier::modemRemoved, this, &SignalIndicator::updateModemManagerModem); 0025 0026 connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionAdded, this, &SignalIndicator::mobileDataEnabledChanged); 0027 connect(NetworkManager::settingsNotifier(), &NetworkManager::SettingsNotifier::connectionRemoved, this, &SignalIndicator::mobileDataEnabledChanged); 0028 connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionAdded, this, &SignalIndicator::mobileDataEnabledChanged); 0029 connect(NetworkManager::notifier(), &NetworkManager::Notifier::activeConnectionRemoved, this, &SignalIndicator::mobileDataEnabledChanged); 0030 connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceAdded, this, &SignalIndicator::updateNetworkManagerModem); 0031 connect(NetworkManager::notifier(), &NetworkManager::Notifier::deviceRemoved, this, &SignalIndicator::updateNetworkManagerModem); 0032 0033 updateModemManagerModem(); 0034 } 0035 0036 int SignalIndicator::strength() const 0037 { 0038 if (!m_modem) { 0039 return 0; 0040 } 0041 return m_modem->signalQuality().signal; 0042 } 0043 0044 QString SignalIndicator::name() const 0045 { 0046 return m_3gppModem ? m_3gppModem->operatorName() : QString(); 0047 } 0048 0049 bool SignalIndicator::modemAvailable() const 0050 { 0051 return !m_modem.isNull(); 0052 } 0053 0054 bool SignalIndicator::simLocked() const 0055 { 0056 if (!m_modem) { 0057 return false; 0058 } 0059 return m_modem->unlockRequired() == MM_MODEM_LOCK_SIM_PIN; 0060 } 0061 0062 bool SignalIndicator::simEmpty() const 0063 { 0064 return !m_modemDevice || !m_modemDevice->sim() || (m_modemDevice->sim()->uni() == QStringLiteral("/")); 0065 } 0066 0067 bool SignalIndicator::mobileDataSupported() const 0068 { 0069 return m_nmModem && !simEmpty(); 0070 } 0071 0072 bool SignalIndicator::mobileDataEnabled() const 0073 { 0074 // no modem -> no mobile data -> report disabled 0075 if (!m_nmModem) { 0076 return false; 0077 } 0078 0079 // mobile data already activated -> report enabled 0080 if (m_nmModem->state() == NetworkManager::Device::Activated) { 0081 return true; 0082 } 0083 0084 // autoconnect disabled on the entire modem -> report disabled 0085 if (!m_nmModem->autoconnect()) { 0086 return false; 0087 } 0088 0089 // at least one connection set to autoconnect -> report enabled 0090 for (NetworkManager::Connection::Ptr con : m_nmModem->availableConnections()) { 0091 if (con->settings()->autoconnect()) { 0092 return true; 0093 } 0094 } 0095 0096 // modem, but no connection, set to autoconnect -> report disabled (#182) 0097 return false; 0098 } 0099 0100 bool SignalIndicator::needsAPNAdded() const 0101 { 0102 return m_nmModem && mobileDataSupported() && m_nmModem->availableConnections().count() == 0; 0103 } 0104 0105 void SignalIndicator::setMobileDataEnabled(bool enabled) 0106 { 0107 if (!m_nmModem) { 0108 return; 0109 } 0110 if (!enabled) { 0111 m_nmModem->setAutoconnect(false); 0112 // we need to also set all connections to not autoconnect (#182) 0113 for (NetworkManager::Connection::Ptr con : m_nmModem->availableConnections()) { 0114 con->settings()->setAutoconnect(false); 0115 con->update(con->settings()->toMap()); 0116 } 0117 m_nmModem->disconnectInterface(); 0118 } else { 0119 m_nmModem->setAutoconnect(true); 0120 // activate the connection that was last used 0121 QDateTime latestTimestamp; 0122 NetworkManager::Connection::Ptr latestCon; 0123 for (NetworkManager::Connection::Ptr con : m_nmModem->availableConnections()) { 0124 QDateTime timestamp = con->settings()->timestamp(); 0125 // if con was not used yet, skip it, otherwise: 0126 // if we have no latestTimestamp yet, con is the latest 0127 // otherwise, compare the timestamps 0128 // in case of a tie, use the first connection that was found 0129 if (!timestamp.isNull() && (latestTimestamp.isNull() || timestamp > latestTimestamp)) { 0130 latestTimestamp = timestamp; 0131 latestCon = con; 0132 } 0133 } 0134 // if we found the last used connection 0135 if (!latestCon.isNull()) { 0136 // set it to autoconnect and connect it immediately 0137 latestCon->settings()->setAutoconnect(true); 0138 latestCon->update(latestCon->settings()->toMap()); 0139 NetworkManager::activateConnection(latestCon->path(), m_nmModem->uni(), ""); 0140 } 0141 } 0142 } 0143 0144 QString SignalIndicator::activeConnectionUni() const 0145 { 0146 if (m_nmModem && m_nmModem->activeConnection() && m_nmModem->activeConnection()->connection()) { 0147 return m_nmModem->activeConnection()->connection()->uuid(); 0148 } 0149 return QString(); 0150 } 0151 0152 QList<ProfileSettings *> &SignalIndicator::profileList() 0153 { 0154 return m_profileList; 0155 } 0156 0157 void SignalIndicator::refreshProfiles() 0158 { 0159 m_profileList.clear(); 0160 0161 if (!m_nmModem) { 0162 Q_EMIT profileListChanged(); 0163 qWarning() << "No NetworkManager modem found, cannot refresh profiles."; 0164 return; 0165 } 0166 0167 for (auto connection : m_nmModem->availableConnections()) { 0168 for (auto setting : connection->settings()->settings()) { 0169 if (setting.dynamicCast<NetworkManager::GsmSetting>()) { 0170 m_profileList.append(new ProfileSettings(this, setting.dynamicCast<NetworkManager::GsmSetting>(), connection)); 0171 } 0172 } 0173 } 0174 Q_EMIT profileListChanged(); 0175 } 0176 0177 void SignalIndicator::activateProfile(const QString &connectionUni) 0178 { 0179 if (!m_nmModem) { 0180 qWarning() << "Cannot activate profile since there is no NetworkManager modem"; 0181 return; 0182 } 0183 0184 qDebug() << QStringLiteral("Activating profile on modem") << m_nmModem->uni() << QStringLiteral("for connection") << connectionUni << "."; 0185 0186 NetworkManager::Connection::Ptr con; 0187 0188 // disable autoconnect for all other connections 0189 for (auto connection : m_nmModem->availableConnections()) { 0190 if (connection->uuid() == connectionUni) { 0191 connection->settings()->setAutoconnect(true); 0192 con = connection; 0193 } else { 0194 connection->settings()->setAutoconnect(false); 0195 } 0196 } 0197 0198 if (!con) { 0199 qDebug() << QStringLiteral("Connection") << connectionUni << QStringLiteral("not found."); 0200 return; 0201 } 0202 0203 // activate connection manually 0204 // despite the documentation saying otherwise, activateConnection seems to need the DBus path, not uuid of the connection 0205 QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::activateConnection(con->path(), m_nmModem->uni(), {}); 0206 reply.waitForFinished(); 0207 if (reply.isError()) { 0208 qWarning() << QStringLiteral("Error activating connection:") << reply.error().message(); 0209 return; 0210 } 0211 } 0212 0213 void SignalIndicator::addProfile(const QString &name, const QString &apn, const QString &username, const QString &password, const QString &networkType) 0214 { 0215 if (!m_nmModem) { 0216 qWarning() << "Cannot add profile since there is no NetworkManager modem"; 0217 return; 0218 } 0219 0220 NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Gsm)}; 0221 settings->setId(name); 0222 settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); 0223 settings->setAutoconnect(true); 0224 settings->addToPermissions(KUser().loginName(), QString()); 0225 0226 NetworkManager::GsmSetting::Ptr gsmSetting = settings->setting(NetworkManager::Setting::Gsm).dynamicCast<NetworkManager::GsmSetting>(); 0227 gsmSetting->setApn(apn); 0228 gsmSetting->setUsername(username); 0229 gsmSetting->setPassword(password); 0230 gsmSetting->setPasswordFlags(password.isEmpty() ? NetworkManager::Setting::NotRequired : NetworkManager::Setting::AgentOwned); 0231 gsmSetting->setNetworkType(ProfileSettings::networkTypeFlag(networkType)); 0232 0233 gsmSetting->setInitialized(true); 0234 0235 QDBusPendingReply<QDBusObjectPath> reply = NetworkManager::addAndActivateConnection(settings->toMap(), m_nmModem->uni(), {}); 0236 reply.waitForFinished(); 0237 if (reply.isError()) { 0238 qWarning() << "Error adding connection:" << reply.error().message(); 0239 } else { 0240 qDebug() << "Successfully added a new connection" << name << "with APN" << apn << "."; 0241 } 0242 } 0243 0244 void SignalIndicator::removeProfile(const QString &connectionUni) 0245 { 0246 NetworkManager::Connection::Ptr con = NetworkManager::findConnectionByUuid(connectionUni); 0247 if (!con) { 0248 qWarning() << "Could not find connection" << connectionUni << "to update!"; 0249 return; 0250 } 0251 0252 QDBusPendingReply reply = con->remove(); 0253 reply.waitForFinished(); 0254 if (reply.isError()) { 0255 qWarning() << "Error removing connection" << reply.error().message(); 0256 } 0257 } 0258 0259 void SignalIndicator::updateProfile(const QString &connectionUni, 0260 const QString &name, 0261 const QString &apn, 0262 const QString &username, 0263 const QString &password, 0264 const QString &networkType) 0265 { 0266 NetworkManager::Connection::Ptr con = NetworkManager::findConnectionByUuid(connectionUni); 0267 if (!con) { 0268 qWarning() << "Could not find connection" << connectionUni << "to update!"; 0269 return; 0270 } 0271 0272 NetworkManager::ConnectionSettings::Ptr conSettings = con->settings(); 0273 if (!conSettings) { 0274 qWarning() << "Could not find connection settings for" << connectionUni << "to update!"; 0275 return; 0276 } 0277 0278 conSettings->setId(name); 0279 0280 NetworkManager::GsmSetting::Ptr gsmSetting = conSettings->setting(NetworkManager::Setting::Gsm).dynamicCast<NetworkManager::GsmSetting>(); 0281 gsmSetting->setApn(apn); 0282 gsmSetting->setUsername(username); 0283 gsmSetting->setPassword(password); 0284 gsmSetting->setPasswordFlags(password.isEmpty() ? NetworkManager::Setting::NotRequired : NetworkManager::Setting::AgentOwned); 0285 gsmSetting->setNetworkType(ProfileSettings::networkTypeFlag(networkType)); 0286 0287 gsmSetting->setInitialized(true); 0288 0289 QDBusPendingReply reply = con->update(conSettings->toMap()); 0290 reply.waitForFinished(); 0291 if (reply.isError()) { 0292 qWarning() << "Error updating connection settings for" << connectionUni << ":" << reply.error().message() << "."; 0293 } else { 0294 qDebug() << "Successfully updated connection settings" << connectionUni << "."; 0295 } 0296 } 0297 0298 void SignalIndicator::updateModemManagerModem() 0299 { 0300 m_modemDevice = nullptr; 0301 m_modem = nullptr; 0302 m_3gppModem = nullptr; 0303 0304 if (ModemManager::modemDevices().isEmpty()) { 0305 qWarning() << "No modems available"; 0306 return; 0307 } 0308 0309 // TODO: we assume that there is a single modem for the time being 0310 m_modemDevice = ModemManager::modemDevices()[0]; 0311 m_modem = m_modemDevice->modemInterface(); 0312 m_3gppModem = m_modemDevice->interface(ModemManager::ModemDevice::GsmInterface).objectCast<ModemManager::Modem3gpp>(); 0313 0314 connect(m_modemDevice->sim().get(), &ModemManager::Sim::simIdentifierChanged, this, &SignalIndicator::simEmptyChanged); 0315 0316 if (m_modem) { 0317 connect(m_modem.get(), &ModemManager::Modem::signalQualityChanged, this, &SignalIndicator::strengthChanged); 0318 connect(m_modem.get(), &ModemManager::Modem::unlockRequiredChanged, this, &SignalIndicator::simLockedChanged); 0319 } 0320 if (m_3gppModem) { 0321 connect(m_3gppModem.get(), &ModemManager::Modem3gpp::operatorNameChanged, this, &SignalIndicator::nameChanged); 0322 } 0323 0324 updateNetworkManagerModem(); 0325 0326 Q_EMIT nameChanged(); 0327 Q_EMIT strengthChanged(); 0328 Q_EMIT modemAvailableChanged(); 0329 } 0330 0331 void SignalIndicator::updateNetworkManagerModem() 0332 { 0333 m_nmModem = nullptr; 0334 if (!m_modemDevice) { 0335 return; 0336 } 0337 0338 // find networkmanager modem 0339 for (NetworkManager::Device::Ptr nmDevice : NetworkManager::networkInterfaces()) { 0340 if (nmDevice->udi() == m_modemDevice->uni()) { 0341 m_nmModem = nmDevice.objectCast<NetworkManager::ModemDevice>(); 0342 0343 connect(m_nmModem.get(), &NetworkManager::Device::autoconnectChanged, this, &SignalIndicator::mobileDataEnabledChanged); 0344 connect(m_nmModem.get(), &NetworkManager::Device::stateChanged, this, &SignalIndicator::mobileDataEnabledChanged); 0345 connect(m_nmModem.get(), &NetworkManager::Device::availableConnectionAppeared, this, &SignalIndicator::mobileDataEnabledChanged); 0346 connect(m_nmModem.get(), &NetworkManager::Device::availableConnectionDisappeared, this, &SignalIndicator::mobileDataEnabledChanged); 0347 0348 connect(m_nmModem.data(), &NetworkManager::ModemDevice::availableConnectionChanged, this, &SignalIndicator::refreshProfiles); 0349 connect(m_nmModem.data(), &NetworkManager::ModemDevice::activeConnectionChanged, this, [this]() -> void { 0350 refreshProfiles(); 0351 Q_EMIT activeConnectionUniChanged(); 0352 }); 0353 0354 refreshProfiles(); 0355 } 0356 } 0357 0358 Q_EMIT mobileDataSupportedChanged(); 0359 Q_EMIT mobileDataEnabledChanged(); 0360 }