1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qconnmanengine.h"
41#include "qconnmanservice_linux_p.h"
42#include "../qnetworksession_impl.h"
43
44#include <QtNetwork/private/qnetworkconfiguration_p.h>
45
46#include <QtNetwork/qnetworksession.h>
47
48#include <QtCore/qdebug.h>
49#include <QtCore/private/qlocking_p.h>
50
51#include <QtDBus/QtDBus>
52#include <QtDBus/QDBusConnection>
53#include <QtDBus/QDBusInterface>
54#include <QtDBus/QDBusMessage>
55#include <QtDBus/QDBusReply>
56#ifndef QT_NO_DBUS
57
58QT_BEGIN_NAMESPACE
59
60QConnmanEngine::QConnmanEngine(QObject *parent)
61: QBearerEngineImpl(parent),
62 connmanManager(new QConnmanManagerInterface(this)),
63 ofonoManager(new QOfonoManagerInterface(this)),
64 ofonoNetwork(0),
65 ofonoContextManager(0)
66{
67 qDBusRegisterMetaType<ConnmanMap>();
68 qDBusRegisterMetaType<ConnmanMapList>();
69 qRegisterMetaType<ConnmanMapList>(typeName: "ConnmanMapList");
70}
71
72QConnmanEngine::~QConnmanEngine()
73{
74}
75
76bool QConnmanEngine::connmanAvailable() const
77{
78 const auto locker = qt_scoped_lock(mutex);
79 return connmanManager->isValid();
80}
81
82void QConnmanEngine::initialize()
83{
84 const auto locker = qt_scoped_lock(mutex);
85 connect(sender: ofonoManager,SIGNAL(modemChanged()),receiver: this,SLOT(changedModem()));
86
87 ofonoNetwork = new QOfonoNetworkRegistrationInterface(ofonoManager->currentModem(),this);
88 ofonoContextManager = new QOfonoDataConnectionManagerInterface(ofonoManager->currentModem(),this);
89 connect(sender: ofonoContextManager,SIGNAL(roamingAllowedChanged(bool)),receiver: this,SLOT(reEvaluateCellular()));
90
91 connect(sender: connmanManager,SIGNAL(servicesChanged(ConnmanMapList,QList<QDBusObjectPath>)),
92 receiver: this, SLOT(updateServices(ConnmanMapList,QList<QDBusObjectPath>)));
93
94 connect(sender: connmanManager,SIGNAL(servicesReady(QStringList)),receiver: this,SLOT(servicesReady(QStringList)));
95 connect(sender: connmanManager,SIGNAL(scanFinished(bool)),receiver: this,SLOT(finishedScan(bool)));
96
97 const auto servPaths = connmanManager->getServices();
98 for (const QString &servPath : servPaths)
99 addServiceConfiguration(servicePath: servPath);
100 Q_EMIT updateCompleted();
101}
102
103void QConnmanEngine::changedModem()
104{
105 const auto locker = qt_scoped_lock(mutex);
106 if (ofonoNetwork)
107 delete ofonoNetwork;
108
109 ofonoNetwork = new QOfonoNetworkRegistrationInterface(ofonoManager->currentModem(),this);
110
111 if (ofonoContextManager)
112 delete ofonoContextManager;
113 ofonoContextManager = new QOfonoDataConnectionManagerInterface(ofonoManager->currentModem(),this);
114}
115
116void QConnmanEngine::servicesReady(const QStringList &list)
117{
118 const auto locker = qt_scoped_lock(mutex);
119 for (const QString &servPath : list)
120 addServiceConfiguration(servicePath: servPath);
121
122 Q_EMIT updateCompleted();
123}
124
125QList<QNetworkConfigurationPrivate *> QConnmanEngine::getConfigurations()
126{
127 const auto locker = qt_scoped_lock(mutex);
128 QList<QNetworkConfigurationPrivate *> fetchedConfigurations;
129 QNetworkConfigurationPrivate* cpPriv = 0;
130 const int numFoundConfigurations = foundConfigurations.count();
131 fetchedConfigurations.reserve(alloc: numFoundConfigurations);
132
133 for (int i = 0; i < numFoundConfigurations; ++i) {
134 QNetworkConfigurationPrivate *config = new QNetworkConfigurationPrivate;
135 cpPriv = foundConfigurations.at(i);
136
137 config->name = cpPriv->name;
138 config->isValid = cpPriv->isValid;
139 config->id = cpPriv->id;
140 config->state = cpPriv->state;
141 config->type = cpPriv->type;
142 config->roamingSupported = cpPriv->roamingSupported;
143 config->purpose = cpPriv->purpose;
144 config->bearerType = cpPriv->bearerType;
145
146 fetchedConfigurations.append(t: config);
147 delete config;
148 }
149 return fetchedConfigurations;
150}
151
152QString QConnmanEngine::getInterfaceFromId(const QString &id)
153{
154 const auto locker = qt_scoped_lock(mutex);
155 return configInterfaces.value(akey: id);
156}
157
158bool QConnmanEngine::hasIdentifier(const QString &id)
159{
160 const auto locker = qt_scoped_lock(mutex);
161 return accessPointConfigurations.contains(akey: id);
162}
163
164void QConnmanEngine::connectToId(const QString &id)
165{
166 const auto locker = qt_scoped_lock(mutex);
167
168 QConnmanServiceInterface *serv = connmanServiceInterfaces.value(akey: id);
169
170 if (!serv || !serv->isValid()) {
171 emit connectionError(id, error: QBearerEngineImpl::InterfaceLookupError);
172 } else {
173 if (serv->type() == QLatin1String("cellular")) {
174 if (serv->roaming()) {
175 if (!isRoamingAllowed(context: serv->path())) {
176 emit connectionError(id, error: QBearerEngineImpl::OperationNotSupported);
177 return;
178 }
179 }
180 }
181 if (serv->autoConnect())
182 serv->connect();
183 }
184}
185
186void QConnmanEngine::disconnectFromId(const QString &id)
187{
188 const auto locker = qt_scoped_lock(mutex);
189 QConnmanServiceInterface *serv = connmanServiceInterfaces.value(akey: id);
190
191 if (!serv || !serv->isValid()) {
192 emit connectionError(id, error: DisconnectionError);
193 } else {
194 serv->disconnect();
195 }
196}
197
198void QConnmanEngine::requestUpdate()
199{
200 const auto locker = qt_scoped_lock(mutex);
201 QTimer::singleShot(msec: 0, receiver: this, SLOT(doRequestUpdate()));
202}
203
204void QConnmanEngine::doRequestUpdate()
205{
206 bool scanned = connmanManager->requestScan(type: "wifi");
207 if (!scanned)
208 Q_EMIT updateCompleted();
209}
210
211void QConnmanEngine::finishedScan(bool error)
212{
213 if (error)
214 Q_EMIT updateCompleted();
215}
216
217void QConnmanEngine::updateServices(const ConnmanMapList &changed, const QList<QDBusObjectPath> &removed)
218{
219 const auto locker = qt_scoped_lock(mutex);
220
221 foreach (const QDBusObjectPath &objectPath, removed) {
222 removeConfiguration(servicePath: objectPath.path());
223 }
224
225 foreach (const ConnmanMap &connmanMap, changed) {
226 const QString id = connmanMap.objectPath.path();
227 if (accessPointConfigurations.contains(akey: id)) {
228 configurationChange(service: connmanServiceInterfaces.value(akey: id));
229 } else {
230 addServiceConfiguration(servicePath: connmanMap.objectPath.path());
231 }
232 }
233 Q_EMIT updateCompleted();
234}
235
236QNetworkSession::State QConnmanEngine::sessionStateForId(const QString &id)
237{
238 const auto locker = qt_scoped_lock(mutex);
239
240 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(akey: id);
241
242 if (!ptr || !ptr->isValid)
243 return QNetworkSession::Invalid;
244
245 QString service = id;
246 QConnmanServiceInterface *serv = connmanServiceInterfaces.value(akey: service);
247 if (!serv)
248 return QNetworkSession::Invalid;
249
250 QString servState = serv->state();
251
252 if (serv->favorite() && (servState == QLatin1String("idle") || servState == QLatin1String("failure"))) {
253 return QNetworkSession::Disconnected;
254 }
255
256 if (servState == QLatin1String("association") || servState == QLatin1String("configuration")) {
257 return QNetworkSession::Connecting;
258 }
259
260 if (servState == QLatin1String("online") || servState == QLatin1String("ready")) {
261 return QNetworkSession::Connected;
262 }
263
264 if ((ptr->state & QNetworkConfiguration::Discovered) ==
265 QNetworkConfiguration::Discovered) {
266 return QNetworkSession::Disconnected;
267 } else if ((ptr->state & QNetworkConfiguration::Defined) == QNetworkConfiguration::Defined) {
268 return QNetworkSession::NotAvailable;
269 } else if ((ptr->state & QNetworkConfiguration::Undefined) ==
270 QNetworkConfiguration::Undefined) {
271 return QNetworkSession::NotAvailable;
272 }
273
274 return QNetworkSession::Invalid;
275}
276
277quint64 QConnmanEngine::bytesWritten(const QString &id)
278{//TODO use connman counter API
279 const auto locker = qt_scoped_lock(mutex);
280 quint64 result = 0;
281 QString devFile = getInterfaceFromId(id);
282 QFile tx("/sys/class/net/"+devFile+"/statistics/tx_bytes");
283 if (tx.open(flags: QIODevice::ReadOnly | QIODevice::Text)) {
284 QTextStream in(&tx);
285 in >> result;
286 tx.close();
287 }
288
289 return result;
290}
291
292quint64 QConnmanEngine::bytesReceived(const QString &id)
293{//TODO use connman counter API
294 const auto locker = qt_scoped_lock(mutex);
295 quint64 result = 0;
296 QString devFile = getInterfaceFromId(id);
297 QFile rx("/sys/class/net/"+devFile+"/statistics/rx_bytes");
298 if (rx.open(flags: QIODevice::ReadOnly | QIODevice::Text)) {
299 QTextStream in(&rx);
300 in >> result;
301 rx.close();
302 }
303 return result;
304}
305
306quint64 QConnmanEngine::startTime(const QString &/*id*/)
307{
308 // TODO
309 const auto locker = qt_scoped_lock(mutex);
310 if (activeTime.isNull()) {
311 return 0;
312 }
313 return activeTime.secsTo(QDateTime::currentDateTime());
314}
315
316QNetworkConfigurationManager::Capabilities QConnmanEngine::capabilities() const
317{
318 return QNetworkConfigurationManager::ForcedRoaming |
319 QNetworkConfigurationManager::DataStatistics |
320 QNetworkConfigurationManager::CanStartAndStopInterfaces |
321 QNetworkConfigurationManager::NetworkSessionRequired;
322}
323
324QNetworkSessionPrivate *QConnmanEngine::createSessionBackend()
325{
326 return new QNetworkSessionPrivateImpl;
327}
328
329QNetworkConfigurationPrivatePointer QConnmanEngine::defaultConfiguration()
330{
331 const auto locker = qt_scoped_lock(mutex);
332 const auto servPaths = connmanManager->getServices();
333 for (const QString &servPath : servPaths) {
334 if (connmanServiceInterfaces.contains(akey: servPath)) {
335 if (accessPointConfigurations.contains(akey: servPath))
336 return accessPointConfigurations.value(akey: servPath);
337 }
338 }
339 return QNetworkConfigurationPrivatePointer();
340}
341
342void QConnmanEngine::serviceStateChanged(const QString &state)
343{
344 QConnmanServiceInterface *service = qobject_cast<QConnmanServiceInterface *>(object: sender());
345 configurationChange(service);
346
347 if (state == QLatin1String("failure")) {
348 emit connectionError(id: service->path(), error: ConnectError);
349 }
350}
351
352void QConnmanEngine::configurationChange(QConnmanServiceInterface *serv)
353{
354 if (!serv)
355 return;
356 auto locker = qt_unique_lock(mutex);
357 QString id = serv->path();
358
359 if (accessPointConfigurations.contains(akey: id)) {
360 bool changed = false;
361 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(akey: id);
362 QString networkName = serv->name();
363 QNetworkConfiguration::StateFlags curState = getStateForService(service: serv->path());
364 ptr->mutex.lock();
365
366 if (!ptr->isValid) {
367 ptr->isValid = true;
368 }
369
370 if (ptr->name != networkName) {
371 ptr->name = networkName;
372 changed = true;
373 }
374
375 if (ptr->state != curState) {
376 ptr->state = curState;
377 changed = true;
378 }
379
380 ptr->mutex.unlock();
381
382 if (changed) {
383 locker.unlock();
384 emit configurationChanged(config: ptr);
385 locker.lock();
386 }
387 }
388
389 locker.unlock();
390 emit updateCompleted();
391}
392
393QNetworkConfiguration::StateFlags QConnmanEngine::getStateForService(const QString &service)
394{
395 const auto locker = qt_scoped_lock(mutex);
396 QConnmanServiceInterface *serv = connmanServiceInterfaces.value(akey: service);
397 if (!serv)
398 return QNetworkConfiguration::Undefined;
399
400 QString state = serv->state();
401 QNetworkConfiguration::StateFlags flag = QNetworkConfiguration::Defined;
402
403 if (serv->type() == QLatin1String("cellular")) {
404
405 if (!serv->autoConnect()|| (serv->roaming()
406 && !isRoamingAllowed(context: serv->path()))) {
407 flag = (flag | QNetworkConfiguration::Defined);
408 } else {
409 flag = (flag | QNetworkConfiguration::Discovered);
410 }
411 } else {
412 if (serv->favorite()) {
413 if (serv->autoConnect()) {
414 flag = (flag | QNetworkConfiguration::Discovered);
415 }
416 } else {
417 flag = QNetworkConfiguration::Undefined;
418 }
419 }
420 if (state == QLatin1String("online") || state == QLatin1String("ready")) {
421 flag = (flag | QNetworkConfiguration::Active);
422 }
423
424 return flag;
425}
426
427QNetworkConfiguration::BearerType QConnmanEngine::typeToBearer(const QString &type)
428{
429 if (type == QLatin1String("wifi"))
430 return QNetworkConfiguration::BearerWLAN;
431 if (type == QLatin1String("ethernet"))
432 return QNetworkConfiguration::BearerEthernet;
433 if (type == QLatin1String("bluetooth"))
434 return QNetworkConfiguration::BearerBluetooth;
435 if (type == QLatin1String("cellular")) {
436 return ofonoTechToBearerType(type);
437 }
438 if (type == QLatin1String("wimax"))
439 return QNetworkConfiguration::BearerWiMAX;
440
441 return QNetworkConfiguration::BearerUnknown;
442}
443
444QNetworkConfiguration::BearerType QConnmanEngine::ofonoTechToBearerType(const QString &/*type*/)
445{
446 if (ofonoNetwork) {
447 QString currentTechnology = ofonoNetwork->getTechnology();
448 if (currentTechnology == QLatin1String("gsm")) {
449 return QNetworkConfiguration::Bearer2G;
450 } else if (currentTechnology == QLatin1String("edge")) {
451 return QNetworkConfiguration::BearerCDMA2000; //wrong, I know
452 } else if (currentTechnology == QLatin1String("umts")) {
453 return QNetworkConfiguration::BearerWCDMA;
454 } else if (currentTechnology == QLatin1String("hspa")) {
455 return QNetworkConfiguration::BearerHSPA;
456 } else if (currentTechnology == QLatin1String("lte")) {
457 return QNetworkConfiguration::BearerLTE;
458 }
459 }
460 return QNetworkConfiguration::BearerUnknown;
461}
462
463bool QConnmanEngine::isRoamingAllowed(const QString &context)
464{
465 const auto dcPaths = ofonoContextManager->contexts();
466 for (const QString &dcPath : dcPaths) {
467 if (dcPath.contains(s: context.section(in_sep: "_",start: -1))) {
468 return ofonoContextManager->roamingAllowed();
469 }
470 }
471 return false;
472}
473
474void QConnmanEngine::removeConfiguration(const QString &id)
475{
476 auto locker = qt_unique_lock(mutex);
477
478 if (accessPointConfigurations.contains(akey: id)) {
479
480 disconnect(sender: connmanServiceInterfaces.value(akey: id),SIGNAL(stateChanged(QString)),
481 receiver: this,SLOT(serviceStateChanged(QString)));
482 serviceNetworks.removeOne(t: id);
483 QConnmanServiceInterface *service = connmanServiceInterfaces.take(akey: id);
484 delete service;
485 QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.take(akey: id);
486 foundConfigurations.removeOne(t: ptr.data());
487 locker.unlock();
488 emit configurationRemoved(config: ptr);
489 }
490}
491
492void QConnmanEngine::addServiceConfiguration(const QString &servicePath)
493{
494 auto locker = qt_unique_lock(mutex);
495 if (!connmanServiceInterfaces.contains(akey: servicePath)) {
496 QConnmanServiceInterface *serv = new QConnmanServiceInterface(servicePath, this);
497 connmanServiceInterfaces.insert(akey: serv->path(),avalue: serv);
498 }
499
500 if (!accessPointConfigurations.contains(akey: servicePath)) {
501
502 serviceNetworks.append(t: servicePath);
503
504 connect(sender: connmanServiceInterfaces.value(akey: servicePath),SIGNAL(stateChanged(QString)),
505 receiver: this,SLOT(serviceStateChanged(QString)));
506
507 QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate();
508 QConnmanServiceInterface *service = connmanServiceInterfaces.value(akey: servicePath);
509
510 QString networkName = service->name();
511
512 const QString connectionType = service->type();
513 if (connectionType == QLatin1String("ethernet")) {
514 cpPriv->bearerType = QNetworkConfiguration::BearerEthernet;
515 } else if (connectionType == QLatin1String("wifi")) {
516 cpPriv->bearerType = QNetworkConfiguration::BearerWLAN;
517 } else if (connectionType == QLatin1String("cellular")) {
518 cpPriv->bearerType = ofonoTechToBearerType(QLatin1String("cellular"));
519 cpPriv->roamingSupported = service->roaming() && isRoamingAllowed(context: servicePath);
520 } else if (connectionType == QLatin1String("wimax")) {
521 cpPriv->bearerType = QNetworkConfiguration::BearerWiMAX;
522 } else {
523 cpPriv->bearerType = QNetworkConfiguration::BearerUnknown;
524 }
525
526 cpPriv->name = networkName;
527 cpPriv->isValid = true;
528 cpPriv->id = servicePath;
529 cpPriv->type = QNetworkConfiguration::InternetAccessPoint;
530
531 if (service->security() == QLatin1String("none")) {
532 cpPriv->purpose = QNetworkConfiguration::PublicPurpose;
533 } else {
534 cpPriv->purpose = QNetworkConfiguration::PrivatePurpose;
535 }
536
537 cpPriv->state = getStateForService(service: servicePath);
538
539 QNetworkConfigurationPrivatePointer ptr(cpPriv);
540 accessPointConfigurations.insert(akey: ptr->id, avalue: ptr);
541 if (connectionType == QLatin1String("cellular")) {
542 foundConfigurations.append(t: cpPriv);
543 } else {
544 foundConfigurations.prepend(t: cpPriv);
545 }
546 configInterfaces[cpPriv->id] = service->serviceInterface();
547
548 locker.unlock();
549 Q_EMIT configurationAdded(config: ptr);
550 }
551}
552
553bool QConnmanEngine::requiresPolling() const
554{
555 return false;
556}
557
558void QConnmanEngine::reEvaluateCellular()
559{
560 const auto servicePaths = connmanManager->getServices();
561 for (const QString &servicePath : servicePaths) {
562 if (servicePath.contains(s: "cellular") && accessPointConfigurations.contains(akey: servicePath)) {
563 configurationChange(serv: connmanServiceInterfaces.value(akey: servicePath));
564 }
565 }
566}
567
568QT_END_NAMESPACE
569
570#endif // QT_NO_DBUS
571

source code of qtbase/src/plugins/bearer/connman/qconnmanengine.cpp