// SPDX-FileCopyrightText: 2023 Devin Lin // SPDX-License-Identifier: GPL-2.0-or-later OR LicenseRef-KDE-Accepted-GPL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "autodetectapn.h" K_PLUGIN_FACTORY_WITH_JSON(StartFactory, "kded_plasma_mobile_autodetectapn.json", registerPlugin();) static const QLoggingCategory LOGGING_CATEGORY("plasma-mobile-autodetectapn"); AutoDetectAPN::AutoDetectAPN(QObject *parent, const QList &) : KDEDModule{parent} { checkAndAddAutodetectedAPN(); } QCoro::Task AutoDetectAPN::checkAndAddAutodetectedAPN() { if (!KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))) { qCDebug(LOGGING_CATEGORY) << "Not running APN autodetection because this is not a Plasma Mobile session..."; co_return; } qCDebug(LOGGING_CATEGORY) << "Running APN autodetection..."; for (ModemManager::ModemDevice::Ptr mmDevice : ModemManager::modemDevices()) { ModemManager::Modem::Ptr mmModem = mmDevice->modemInterface(); if (!mmModem) { continue; } const NetworkManager::ModemDevice::Ptr nmModem = findNMModem(mmModem); const ModemManager::Sim::Ptr mmSim = mmDevice->sim(); if (!nmModem || !mmSim) { continue; } // Detect whether the modem already has an APN // TODO: currently just check if there are any NM connections, this doesn't work if the user swapped out their SIM. // we need something that detects when this occurs if (!nmModem->availableConnections().empty()) { qCDebug(LOGGING_CATEGORY) << "Modem" << nmModem->uni() << "already has a connection configured"; continue; } // MCCMNC value const QString operatorCode = mmSim->operatorIdentifier(); const QString gid1 = mmSim->gid1(); // for carriers using MVNO, which could cause duplicate MCCMNC values const QString spn = mmSim->operatorName(); const QString imsi = mmSim->imsi(); // Autodetect an APN std::optional detectedAPNOpt = findAPN(operatorCode, gid1, spn, imsi); if (detectedAPNOpt == std::nullopt || (*detectedAPNOpt).apn.isEmpty()) { qCDebug(LOGGING_CATEGORY) << "Could not find an APN for the SIM with code" << operatorCode; continue; } APNEntry detectedAPN = *detectedAPNOpt; // Create connection NetworkManager::ConnectionSettings::Ptr settings{new NetworkManager::ConnectionSettings(NetworkManager::ConnectionSettings::Gsm)}; settings->setId(detectedAPN.carrier); settings->setUuid(NetworkManager::ConnectionSettings::createNewUuid()); settings->setAutoconnect(true); settings->addToPermissions(KUser().loginName(), QString()); NetworkManager::GsmSetting::Ptr gsmSetting = settings->setting(NetworkManager::Setting::Gsm).dynamicCast(); gsmSetting->setApn(detectedAPN.apn); gsmSetting->setPasswordFlags(NetworkManager::Setting::NotRequired); gsmSetting->setNetworkType(NetworkManager::GsmSetting::NetworkType::Prefer4GLte); gsmSetting->setHomeOnly(false); // TODO respect modem roaming settings? gsmSetting->setInitialized(true); if ( detectedAPN.protocol == QStringLiteral("IPV6") || detectedAPN.protocol == QStringLiteral("IPV4V6") ) { NetworkManager::Ipv6Setting::Ptr ipv6Setting = settings->setting(NetworkManager::Setting::Ipv6).dynamicCast(); ipv6Setting->setMethod(NetworkManager::Ipv6Setting::ConfigMethod::Automatic); ipv6Setting->setInitialized(true); } QDBusReply reply = co_await NetworkManager::addAndActivateConnection(settings->toMap(), nmModem->uni(), ""); if (!reply.isValid()) { qCWarning(LOGGING_CATEGORY) << "Error adding autodetected connection:" << reply.error().message(); } else { qCDebug(LOGGING_CATEGORY) << "Successfully autodetected" << detectedAPN.carrier << "with APN" << detectedAPN.apn << "."; } } } NetworkManager::ModemDevice::Ptr AutoDetectAPN::findNMModem(ModemManager::Modem::Ptr mmModem) { const auto interfaces = NetworkManager::networkInterfaces(); for (const NetworkManager::Device::Ptr &nmDevice : interfaces) { if (nmDevice->udi() == mmModem->uni()) { return nmDevice.objectCast(); } } return nullptr; } std::optional AutoDetectAPN::findAPN(const QString &operatorCode, const QString &gid1, const QString &spn, const QString &imsi) const { const QString providersFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma-mobile-apn-info/apns-full-conf.xml")); QFile file{providersFile}; if (!file.open(QIODevice::ReadOnly)) { return std::nullopt; } QDomDocument document; document.setContent(&file); QDomElement root = document.documentElement(); if (root.isNull()) { return std::nullopt; } QDomNode apns = root.firstChild(); // candidates; QDomNode node = apns.firstChild(); // 0) { return {candidates[0]}; } else { return std::nullopt; } } #include "autodetectapn.moc"