| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qconnmanengine.h" |
| #include "qconnmanservice_linux_p.h" |
| #include "../qnetworksession_impl.h" |
| |
| #include <QtNetwork/private/qnetworkconfiguration_p.h> |
| |
| #include <QtNetwork/qnetworksession.h> |
| |
| #include <QtCore/qdebug.h> |
| #include <QtCore/private/qlocking_p.h> |
| |
| #include <QtDBus/QtDBus> |
| #include <QtDBus/QDBusConnection> |
| #include <QtDBus/QDBusInterface> |
| #include <QtDBus/QDBusMessage> |
| #include <QtDBus/QDBusReply> |
| #ifndef QT_NO_DBUS |
| |
| QT_BEGIN_NAMESPACE |
| |
| QConnmanEngine::QConnmanEngine(QObject *parent) |
| : QBearerEngineImpl(parent), |
| connmanManager(new QConnmanManagerInterface(this)), |
| ofonoManager(new QOfonoManagerInterface(this)), |
| ofonoNetwork(0), |
| ofonoContextManager(0) |
| { |
| qDBusRegisterMetaType<ConnmanMap>(); |
| qDBusRegisterMetaType<ConnmanMapList>(); |
| qRegisterMetaType<ConnmanMapList>("ConnmanMapList"); |
| } |
| |
| QConnmanEngine::~QConnmanEngine() |
| { |
| } |
| |
| bool QConnmanEngine::connmanAvailable() const |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| return connmanManager->isValid(); |
| } |
| |
| void QConnmanEngine::initialize() |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| connect(ofonoManager,SIGNAL(modemChanged()),this,SLOT(changedModem())); |
| |
| ofonoNetwork = new QOfonoNetworkRegistrationInterface(ofonoManager->currentModem(),this); |
| ofonoContextManager = new QOfonoDataConnectionManagerInterface(ofonoManager->currentModem(),this); |
| connect(ofonoContextManager,SIGNAL(roamingAllowedChanged(bool)),this,SLOT(reEvaluateCellular())); |
| |
| connect(connmanManager,SIGNAL(servicesChanged(ConnmanMapList,QList<QDBusObjectPath>)), |
| this, SLOT(updateServices(ConnmanMapList,QList<QDBusObjectPath>))); |
| |
| connect(connmanManager,SIGNAL(servicesReady(QStringList)),this,SLOT(servicesReady(QStringList))); |
| connect(connmanManager,SIGNAL(scanFinished(bool)),this,SLOT(finishedScan(bool))); |
| |
| const auto servPaths = connmanManager->getServices(); |
| for (const QString &servPath : servPaths) |
| addServiceConfiguration(servPath); |
| Q_EMIT updateCompleted(); |
| } |
| |
| void QConnmanEngine::changedModem() |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| if (ofonoNetwork) |
| delete ofonoNetwork; |
| |
| ofonoNetwork = new QOfonoNetworkRegistrationInterface(ofonoManager->currentModem(),this); |
| |
| if (ofonoContextManager) |
| delete ofonoContextManager; |
| ofonoContextManager = new QOfonoDataConnectionManagerInterface(ofonoManager->currentModem(),this); |
| } |
| |
| void QConnmanEngine::servicesReady(const QStringList &list) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| for (const QString &servPath : list) |
| addServiceConfiguration(servPath); |
| |
| Q_EMIT updateCompleted(); |
| } |
| |
| QList<QNetworkConfigurationPrivate *> QConnmanEngine::getConfigurations() |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| QList<QNetworkConfigurationPrivate *> fetchedConfigurations; |
| QNetworkConfigurationPrivate* cpPriv = 0; |
| const int numFoundConfigurations = foundConfigurations.count(); |
| fetchedConfigurations.reserve(numFoundConfigurations); |
| |
| for (int i = 0; i < numFoundConfigurations; ++i) { |
| QNetworkConfigurationPrivate *config = new QNetworkConfigurationPrivate; |
| cpPriv = foundConfigurations.at(i); |
| |
| config->name = cpPriv->name; |
| config->isValid = cpPriv->isValid; |
| config->id = cpPriv->id; |
| config->state = cpPriv->state; |
| config->type = cpPriv->type; |
| config->roamingSupported = cpPriv->roamingSupported; |
| config->purpose = cpPriv->purpose; |
| config->bearerType = cpPriv->bearerType; |
| |
| fetchedConfigurations.append(config); |
| delete config; |
| } |
| return fetchedConfigurations; |
| } |
| |
| QString QConnmanEngine::getInterfaceFromId(const QString &id) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| return configInterfaces.value(id); |
| } |
| |
| bool QConnmanEngine::hasIdentifier(const QString &id) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| return accessPointConfigurations.contains(id); |
| } |
| |
| void QConnmanEngine::connectToId(const QString &id) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| |
| QConnmanServiceInterface *serv = connmanServiceInterfaces.value(id); |
| |
| if (!serv || !serv->isValid()) { |
| emit connectionError(id, QBearerEngineImpl::InterfaceLookupError); |
| } else { |
| if (serv->type() == QLatin1String("cellular")) { |
| if (serv->roaming()) { |
| if (!isRoamingAllowed(serv->path())) { |
| emit connectionError(id, QBearerEngineImpl::OperationNotSupported); |
| return; |
| } |
| } |
| } |
| if (serv->autoConnect()) |
| serv->connect(); |
| } |
| } |
| |
| void QConnmanEngine::disconnectFromId(const QString &id) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| QConnmanServiceInterface *serv = connmanServiceInterfaces.value(id); |
| |
| if (!serv || !serv->isValid()) { |
| emit connectionError(id, DisconnectionError); |
| } else { |
| serv->disconnect(); |
| } |
| } |
| |
| void QConnmanEngine::requestUpdate() |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| QTimer::singleShot(0, this, SLOT(doRequestUpdate())); |
| } |
| |
| void QConnmanEngine::doRequestUpdate() |
| { |
| bool scanned = connmanManager->requestScan("wifi"); |
| if (!scanned) |
| Q_EMIT updateCompleted(); |
| } |
| |
| void QConnmanEngine::finishedScan(bool error) |
| { |
| if (error) |
| Q_EMIT updateCompleted(); |
| } |
| |
| void QConnmanEngine::updateServices(const ConnmanMapList &changed, const QList<QDBusObjectPath> &removed) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| |
| foreach (const QDBusObjectPath &objectPath, removed) { |
| removeConfiguration(objectPath.path()); |
| } |
| |
| foreach (const ConnmanMap &connmanMap, changed) { |
| const QString id = connmanMap.objectPath.path(); |
| if (accessPointConfigurations.contains(id)) { |
| configurationChange(connmanServiceInterfaces.value(id)); |
| } else { |
| addServiceConfiguration(connmanMap.objectPath.path()); |
| } |
| } |
| Q_EMIT updateCompleted(); |
| } |
| |
| QNetworkSession::State QConnmanEngine::sessionStateForId(const QString &id) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| |
| QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); |
| |
| if (!ptr || !ptr->isValid) |
| return QNetworkSession::Invalid; |
| |
| QString service = id; |
| QConnmanServiceInterface *serv = connmanServiceInterfaces.value(service); |
| if (!serv) |
| return QNetworkSession::Invalid; |
| |
| QString servState = serv->state(); |
| |
| if (serv->favorite() && (servState == QLatin1String("idle") || servState == QLatin1String("failure"))) { |
| return QNetworkSession::Disconnected; |
| } |
| |
| if (servState == QLatin1String("association") || servState == QLatin1String("configuration")) { |
| return QNetworkSession::Connecting; |
| } |
| |
| if (servState == QLatin1String("online") || servState == QLatin1String("ready")) { |
| return QNetworkSession::Connected; |
| } |
| |
| if ((ptr->state & QNetworkConfiguration::Discovered) == |
| QNetworkConfiguration::Discovered) { |
| return QNetworkSession::Disconnected; |
| } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) { |
| return QNetworkSession::NotAvailable; |
| } else if ((ptr->state & QNetworkConfiguration::Undefined) == |
| QNetworkConfiguration::Undefined) { |
| return QNetworkSession::NotAvailable; |
| } |
| |
| return QNetworkSession::Invalid; |
| } |
| |
| quint64 QConnmanEngine::bytesWritten(const QString &id) |
| {//TODO use connman counter API |
| const auto locker = qt_scoped_lock(mutex); |
| quint64 result = 0; |
| QString devFile = getInterfaceFromId(id); |
| QFile tx("/sys/class/net/"+devFile+"/statistics/tx_bytes"); |
| if (tx.open(QIODevice::ReadOnly | QIODevice::Text)) { |
| QTextStream in(&tx); |
| in >> result; |
| tx.close(); |
| } |
| |
| return result; |
| } |
| |
| quint64 QConnmanEngine::bytesReceived(const QString &id) |
| {//TODO use connman counter API |
| const auto locker = qt_scoped_lock(mutex); |
| quint64 result = 0; |
| QString devFile = getInterfaceFromId(id); |
| QFile rx("/sys/class/net/"+devFile+"/statistics/rx_bytes"); |
| if (rx.open(QIODevice::ReadOnly | QIODevice::Text)) { |
| QTextStream in(&rx); |
| in >> result; |
| rx.close(); |
| } |
| return result; |
| } |
| |
| quint64 QConnmanEngine::startTime(const QString &/*id*/) |
| { |
| // TODO |
| const auto locker = qt_scoped_lock(mutex); |
| if (activeTime.isNull()) { |
| return 0; |
| } |
| return activeTime.secsTo(QDateTime::currentDateTime()); |
| } |
| |
| QNetworkConfigurationManager::Capabilities QConnmanEngine::capabilities() const |
| { |
| return QNetworkConfigurationManager::ForcedRoaming | |
| QNetworkConfigurationManager::DataStatistics | |
| QNetworkConfigurationManager::CanStartAndStopInterfaces | |
| QNetworkConfigurationManager::NetworkSessionRequired; |
| } |
| |
| QNetworkSessionPrivate *QConnmanEngine::createSessionBackend() |
| { |
| return new QNetworkSessionPrivateImpl; |
| } |
| |
| QNetworkConfigurationPrivatePointer QConnmanEngine::defaultConfiguration() |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| const auto servPaths = connmanManager->getServices(); |
| for (const QString &servPath : servPaths) { |
| if (connmanServiceInterfaces.contains(servPath)) { |
| if (accessPointConfigurations.contains(servPath)) |
| return accessPointConfigurations.value(servPath); |
| } |
| } |
| return QNetworkConfigurationPrivatePointer(); |
| } |
| |
| void QConnmanEngine::serviceStateChanged(const QString &state) |
| { |
| QConnmanServiceInterface *service = qobject_cast<QConnmanServiceInterface *>(sender()); |
| configurationChange(service); |
| |
| if (state == QLatin1String("failure")) { |
| emit connectionError(service->path(), ConnectError); |
| } |
| } |
| |
| void QConnmanEngine::configurationChange(QConnmanServiceInterface *serv) |
| { |
| if (!serv) |
| return; |
| auto locker = qt_unique_lock(mutex); |
| QString id = serv->path(); |
| |
| if (accessPointConfigurations.contains(id)) { |
| bool changed = false; |
| QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(id); |
| QString networkName = serv->name(); |
| QNetworkConfiguration::StateFlags curState = getStateForService(serv->path()); |
| ptr->mutex.lock(); |
| |
| if (!ptr->isValid) { |
| ptr->isValid = true; |
| } |
| |
| if (ptr->name != networkName) { |
| ptr->name = networkName; |
| changed = true; |
| } |
| |
| if (ptr->state != curState) { |
| ptr->state = curState; |
| changed = true; |
| } |
| |
| ptr->mutex.unlock(); |
| |
| if (changed) { |
| locker.unlock(); |
| emit configurationChanged(ptr); |
| locker.lock(); |
| } |
| } |
| |
| locker.unlock(); |
| emit updateCompleted(); |
| } |
| |
| QNetworkConfiguration::StateFlags QConnmanEngine::getStateForService(const QString &service) |
| { |
| const auto locker = qt_scoped_lock(mutex); |
| QConnmanServiceInterface *serv = connmanServiceInterfaces.value(service); |
| if (!serv) |
| return QNetworkConfiguration::Undefined; |
| |
| QString state = serv->state(); |
| QNetworkConfiguration::StateFlags flag = QNetworkConfiguration::Defined; |
| |
| if (serv->type() == QLatin1String("cellular")) { |
| |
| if (!serv->autoConnect()|| (serv->roaming() |
| && !isRoamingAllowed(serv->path()))) { |
| flag = (flag | QNetworkConfiguration::Defined); |
| } else { |
| flag = (flag | QNetworkConfiguration::Discovered); |
| } |
| } else { |
| if (serv->favorite()) { |
| if (serv->autoConnect()) { |
| flag = (flag | QNetworkConfiguration::Discovered); |
| } |
| } else { |
| flag = QNetworkConfiguration::Undefined; |
| } |
| } |
| if (state == QLatin1String("online") || state == QLatin1String("ready")) { |
| flag = (flag | QNetworkConfiguration::Active); |
| } |
| |
| return flag; |
| } |
| |
| QNetworkConfiguration::BearerType QConnmanEngine::typeToBearer(const QString &type) |
| { |
| if (type == QLatin1String("wifi")) |
| return QNetworkConfiguration::BearerWLAN; |
| if (type == QLatin1String("ethernet")) |
| return QNetworkConfiguration::BearerEthernet; |
| if (type == QLatin1String("bluetooth")) |
| return QNetworkConfiguration::BearerBluetooth; |
| if (type == QLatin1String("cellular")) { |
| return ofonoTechToBearerType(type); |
| } |
| if (type == QLatin1String("wimax")) |
| return QNetworkConfiguration::BearerWiMAX; |
| |
| return QNetworkConfiguration::BearerUnknown; |
| } |
| |
| QNetworkConfiguration::BearerType QConnmanEngine::ofonoTechToBearerType(const QString &/*type*/) |
| { |
| if (ofonoNetwork) { |
| QString currentTechnology = ofonoNetwork->getTechnology(); |
| if (currentTechnology == QLatin1String("gsm")) { |
| return QNetworkConfiguration::Bearer2G; |
| } else if (currentTechnology == QLatin1String("edge")) { |
| return QNetworkConfiguration::BearerCDMA2000; //wrong, I know |
| } else if (currentTechnology == QLatin1String("umts")) { |
| return QNetworkConfiguration::BearerWCDMA; |
| } else if (currentTechnology == QLatin1String("hspa")) { |
| return QNetworkConfiguration::BearerHSPA; |
| } else if (currentTechnology == QLatin1String("lte")) { |
| return QNetworkConfiguration::BearerLTE; |
| } |
| } |
| return QNetworkConfiguration::BearerUnknown; |
| } |
| |
| bool QConnmanEngine::isRoamingAllowed(const QString &context) |
| { |
| const auto dcPaths = ofonoContextManager->contexts(); |
| for (const QString &dcPath : dcPaths) { |
| if (dcPath.contains(context.section("_",-1))) { |
| return ofonoContextManager->roamingAllowed(); |
| } |
| } |
| return false; |
| } |
| |
| void QConnmanEngine::removeConfiguration(const QString &id) |
| { |
| auto locker = qt_unique_lock(mutex); |
| |
| if (accessPointConfigurations.contains(id)) { |
| |
| disconnect(connmanServiceInterfaces.value(id),SIGNAL(stateChanged(QString)), |
| this,SLOT(serviceStateChanged(QString))); |
| serviceNetworks.removeOne(id); |
| QConnmanServiceInterface *service = connmanServiceInterfaces.take(id); |
| delete service; |
| QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.take(id); |
| foundConfigurations.removeOne(ptr.data()); |
| locker.unlock(); |
| emit configurationRemoved(ptr); |
| } |
| } |
| |
| void QConnmanEngine::addServiceConfiguration(const QString &servicePath) |
| { |
| auto locker = qt_unique_lock(mutex); |
| if (!connmanServiceInterfaces.contains(servicePath)) { |
| QConnmanServiceInterface *serv = new QConnmanServiceInterface(servicePath, this); |
| connmanServiceInterfaces.insert(serv->path(),serv); |
| } |
| |
| if (!accessPointConfigurations.contains(servicePath)) { |
| |
| serviceNetworks.append(servicePath); |
| |
| connect(connmanServiceInterfaces.value(servicePath),SIGNAL(stateChanged(QString)), |
| this,SLOT(serviceStateChanged(QString))); |
| |
| QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate(); |
| QConnmanServiceInterface *service = connmanServiceInterfaces.value(servicePath); |
| |
| QString networkName = service->name(); |
| |
| const QString connectionType = service->type(); |
| if (connectionType == QLatin1String("ethernet")) { |
| cpPriv->bearerType = QNetworkConfiguration::BearerEthernet; |
| } else if (connectionType == QLatin1String("wifi")) { |
| cpPriv->bearerType = QNetworkConfiguration::BearerWLAN; |
| } else if (connectionType == QLatin1String("cellular")) { |
| cpPriv->bearerType = ofonoTechToBearerType(QLatin1String("cellular")); |
| cpPriv->roamingSupported = service->roaming() && isRoamingAllowed(servicePath); |
| } else if (connectionType == QLatin1String("wimax")) { |
| cpPriv->bearerType = QNetworkConfiguration::BearerWiMAX; |
| } else { |
| cpPriv->bearerType = QNetworkConfiguration::BearerUnknown; |
| } |
| |
| cpPriv->name = networkName; |
| cpPriv->isValid = true; |
| cpPriv->id = servicePath; |
| cpPriv->type = QNetworkConfiguration::InternetAccessPoint; |
| |
| if (service->security() == QLatin1String("none")) { |
| cpPriv->purpose = QNetworkConfiguration::PublicPurpose; |
| } else { |
| cpPriv->purpose = QNetworkConfiguration::PrivatePurpose; |
| } |
| |
| cpPriv->state = getStateForService(servicePath); |
| |
| QNetworkConfigurationPrivatePointer ptr(cpPriv); |
| accessPointConfigurations.insert(ptr->id, ptr); |
| if (connectionType == QLatin1String("cellular")) { |
| foundConfigurations.append(cpPriv); |
| } else { |
| foundConfigurations.prepend(cpPriv); |
| } |
| configInterfaces[cpPriv->id] = service->serviceInterface(); |
| |
| locker.unlock(); |
| Q_EMIT configurationAdded(ptr); |
| } |
| } |
| |
| bool QConnmanEngine::requiresPolling() const |
| { |
| return false; |
| } |
| |
| void QConnmanEngine::reEvaluateCellular() |
| { |
| const auto servicePaths = connmanManager->getServices(); |
| for (const QString &servicePath : servicePaths) { |
| if (servicePath.contains("cellular") && accessPointConfigurations.contains(servicePath)) { |
| configurationChange(connmanServiceInterfaces.value(servicePath)); |
| } |
| } |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_DBUS |