File indexing completed on 2024-05-12 05:36:08

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 }