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 QtNetwork module 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 "qnetworkconfigmanager_p.h"
41#include "qbearerplugin_p.h"
42
43#include <QtCore/qdebug.h>
44#include <QtCore/qtimer.h>
45#include <QtCore/qstringlist.h>
46#include <QtCore/qthread.h>
47#include <QtCore/private/qcoreapplication_p.h>
48#include <QtCore/private/qlocking_p.h>
49#include <QtCore/private/qthread_p.h>
50
51#include <QtCore/qbytearray.h>
52#include <QtCore/qglobal.h>
53
54#include <utility>
55
56
57#ifndef QT_NO_BEARERMANAGEMENT
58
59QT_BEGIN_NAMESPACE
60
61QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
62 : QObject(), pollTimer(nullptr),
63 loader(QBearerEngineFactoryInterface_iid, QLatin1String("/bearer")),
64 forcedPolling(0), firstUpdate(true)
65{
66 qRegisterMetaType<QNetworkConfiguration>();
67 qRegisterMetaType<QNetworkConfigurationPrivatePointer>();
68}
69
70void QNetworkConfigurationManagerPrivate::initialize()
71{
72 //Two stage construction, because we only want to do this heavyweight work for the winner of the Q_GLOBAL_STATIC race.
73 bearerThread = new QDaemonThread();
74 bearerThread->setObjectName(QStringLiteral("Qt bearer thread"));
75
76 bearerThread->moveToThread(thread: QCoreApplicationPrivate::mainThread()); // because cleanup() is called in main thread context.
77 moveToThread(thread: bearerThread);
78 bearerThread->start();
79 updateConfigurations();
80}
81
82QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate()
83{
84 QMutexLocker locker(&mutex);
85
86 qDeleteAll(c: sessionEngines);
87 sessionEngines.clear();
88 if (bearerThread)
89 bearerThread->quit();
90}
91
92void QNetworkConfigurationManagerPrivate::cleanup()
93{
94 QThread* thread = bearerThread;
95 deleteLater();
96 if (thread->wait(deadline: QDeadlineTimer(5000)))
97 delete thread;
98}
99
100QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const
101{
102 QMutexLocker locker(&mutex);
103
104 for (QBearerEngine *engine : sessionEngines) {
105 QNetworkConfigurationPrivatePointer ptr = engine->defaultConfiguration();
106 if (ptr) {
107 QNetworkConfiguration config;
108 config.d = ptr;
109 return config;
110 }
111 }
112
113 // Engines don't have a default configuration.
114
115 // Return first active snap
116 QNetworkConfigurationPrivatePointer defaultConfiguration;
117
118 for (QBearerEngine *engine : sessionEngines) {
119 const auto locker = qt_scoped_lock(mutex&: engine->mutex);
120
121 for (const auto &ptr : qAsConst(t&: engine->snapConfigurations)) {
122 const auto locker = qt_scoped_lock(mutex&: ptr->mutex);
123
124 if ((ptr->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) {
125 QNetworkConfiguration config;
126 config.d = ptr;
127 return config;
128 } else if (!defaultConfiguration) {
129 if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered)
130 defaultConfiguration = ptr;
131 }
132 }
133 }
134
135 // No Active SNAPs return first Discovered SNAP.
136 if (defaultConfiguration) {
137 QNetworkConfiguration config;
138 config.d = defaultConfiguration;
139 return config;
140 }
141
142 /*
143 No Active or Discovered SNAPs, find the perferred access point.
144 The following priority order is used:
145
146 1. Active Ethernet
147 2. Active WLAN
148 3. Active Other
149 4. Discovered Ethernet
150 5. Discovered WLAN
151 6. Discovered Other
152 */
153
154 for (QBearerEngine *engine : sessionEngines) {
155
156 QMutexLocker locker(&engine->mutex);
157
158 for (const auto &ptr : qAsConst(t&: engine->accessPointConfigurations)) {
159
160 QMutexLocker configLocker(&ptr->mutex);
161 QNetworkConfiguration::BearerType bearerType = ptr->bearerType;
162
163 if ((ptr->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) {
164 if (!defaultConfiguration) {
165 defaultConfiguration = ptr;
166 } else {
167 QMutexLocker defaultConfigLocker(&defaultConfiguration->mutex);
168
169 if (defaultConfiguration->state == ptr->state) {
170 switch (defaultConfiguration->bearerType) {
171 case QNetworkConfiguration::BearerEthernet:
172 // do nothing
173 break;
174 case QNetworkConfiguration::BearerWLAN:
175 // Ethernet beats WLAN
176 defaultConfiguration = ptr;
177 break;
178 default:
179 // Ethernet and WLAN beats other
180 if (bearerType == QNetworkConfiguration::BearerEthernet ||
181 bearerType == QNetworkConfiguration::BearerWLAN) {
182 defaultConfiguration = ptr;
183 }
184 }
185 } else {
186 // active beats discovered
187 if ((defaultConfiguration->state & QNetworkConfiguration::Active) !=
188 QNetworkConfiguration::Active) {
189 defaultConfiguration = ptr;
190 }
191 }
192 }
193 }
194 }
195 }
196
197 // No Active InternetAccessPoint return first Discovered InternetAccessPoint.
198 if (defaultConfiguration) {
199 QNetworkConfiguration config;
200 config.d = defaultConfiguration;
201 return config;
202 }
203
204 return QNetworkConfiguration();
205}
206
207QList<QNetworkConfiguration> QNetworkConfigurationManagerPrivate::allConfigurations(QNetworkConfiguration::StateFlags filter) const
208{
209 QList<QNetworkConfiguration> result;
210
211 QMutexLocker locker(&mutex);
212
213 for (QBearerEngine *engine : sessionEngines) {
214
215 const auto locker = qt_scoped_lock(mutex&: engine->mutex);
216
217 //find all InternetAccessPoints
218 for (const auto &ptr : qAsConst(t&: engine->accessPointConfigurations)) {
219 const auto locker = qt_scoped_lock(mutex&: ptr->mutex);
220
221 if ((ptr->state & filter) == filter) {
222 QNetworkConfiguration pt;
223 pt.d = ptr;
224 result << pt;
225 }
226 }
227
228 //find all service networks
229 for (const auto &ptr : qAsConst(t&: engine->snapConfigurations)) {
230 const auto locker = qt_scoped_lock(mutex&: ptr->mutex);
231
232 if ((ptr->state & filter) == filter) {
233 QNetworkConfiguration pt;
234 pt.d = ptr;
235 result << pt;
236 }
237 }
238 }
239
240 return result;
241}
242
243QNetworkConfiguration QNetworkConfigurationManagerPrivate::configurationFromIdentifier(const QString &identifier) const
244{
245 QNetworkConfiguration item;
246
247 const auto locker = qt_scoped_lock(mutex);
248
249 for (QBearerEngine *engine : sessionEngines) {
250 const auto locker = qt_scoped_lock(mutex&: engine->mutex);
251 if (auto ptr = engine->accessPointConfigurations.value(akey: identifier)) {
252 item.d = std::move(ptr);
253 break;
254 }
255 if (auto ptr = engine->snapConfigurations.value(akey: identifier)) {
256 item.d = std::move(ptr);
257 break;
258 }
259 if (auto ptr = engine->userChoiceConfigurations.value(akey: identifier)) {
260 item.d = std::move(ptr);
261 break;
262 }
263 }
264
265 return item;
266}
267
268bool QNetworkConfigurationManagerPrivate::isOnline() const
269{
270 const auto locker = qt_scoped_lock(mutex);
271
272 // We need allConfigurations since onlineConfigurations is filled with queued connections
273 // and thus is not always (more importantly just after creation) up to date
274 return !allConfigurations(filter: QNetworkConfiguration::Active).isEmpty();
275}
276
277QNetworkConfigurationManager::Capabilities QNetworkConfigurationManagerPrivate::capabilities() const
278{
279 const auto locker = qt_scoped_lock(mutex);
280
281 QNetworkConfigurationManager::Capabilities capFlags;
282
283 for (QBearerEngine *engine : sessionEngines)
284 capFlags |= engine->capabilities();
285
286 return capFlags;
287}
288
289void QNetworkConfigurationManagerPrivate::configurationAdded(QNetworkConfigurationPrivatePointer ptr)
290{
291 const auto locker = qt_scoped_lock(mutex);
292
293 if (!firstUpdate) {
294 QNetworkConfiguration item;
295 item.d = ptr;
296 emit configurationAdded(config: item);
297 }
298
299 auto ptrLocker = qt_unique_lock(mutex&: ptr->mutex);
300 if (ptr->state == QNetworkConfiguration::Active) {
301 const auto id = ptr->id;
302 ptrLocker.unlock();
303 onlineConfigurations.insert(value: id);
304 if (!firstUpdate && onlineConfigurations.count() == 1)
305 emit onlineStateChanged(isOnline: true);
306 }
307}
308
309void QNetworkConfigurationManagerPrivate::configurationRemoved(QNetworkConfigurationPrivatePointer ptr)
310{
311 const auto locker = qt_scoped_lock(mutex);
312
313 {
314 const auto locker = qt_scoped_lock(mutex&: ptr->mutex);
315 ptr->isValid = false;
316 }
317
318 if (!firstUpdate) {
319 QNetworkConfiguration item;
320 item.d = ptr;
321 emit configurationRemoved(config: item);
322 }
323
324 onlineConfigurations.remove(value: ptr->id);
325 if (!firstUpdate && onlineConfigurations.isEmpty())
326 emit onlineStateChanged(isOnline: false);
327}
328
329void QNetworkConfigurationManagerPrivate::configurationChanged(QNetworkConfigurationPrivatePointer ptr)
330{
331 const auto locker = qt_scoped_lock(mutex);
332
333 if (!firstUpdate) {
334 QNetworkConfiguration item;
335 item.d = ptr;
336 emit configurationChanged(config: item);
337 }
338
339 bool previous = !onlineConfigurations.isEmpty();
340
341 {
342 const auto locker = qt_scoped_lock(mutex&: ptr->mutex);
343 if (ptr->state == QNetworkConfiguration::Active)
344 onlineConfigurations.insert(value: ptr->id);
345 else
346 onlineConfigurations.remove(value: ptr->id);
347 }
348
349 bool online = !onlineConfigurations.isEmpty();
350
351 if (!firstUpdate && online != previous)
352 emit onlineStateChanged(isOnline: online);
353}
354
355void QNetworkConfigurationManagerPrivate::updateConfigurations()
356{
357 typedef QMultiMap<int, QString> PluginKeyMap;
358 typedef PluginKeyMap::const_iterator PluginKeyMapConstIterator;
359
360 auto locker = qt_unique_lock(mutex);
361
362 if (firstUpdate) {
363 if (qobject_cast<QBearerEngine *>(object: sender()))
364 return;
365
366 updating = false;
367
368 bool envOK = false;
369 const int skipGeneric = qEnvironmentVariableIntValue(varName: "QT_EXCLUDE_GENERIC_BEARER", ok: &envOK);
370 QBearerEngine *generic = nullptr;
371 QFactoryLoader *l = &loader;
372 const PluginKeyMap keyMap = l->keyMap();
373 const PluginKeyMapConstIterator cend = keyMap.constEnd();
374 QStringList addedEngines;
375 for (PluginKeyMapConstIterator it = keyMap.constBegin(); it != cend; ++it) {
376 const QString &key = it.value();
377 if (addedEngines.contains(str: key))
378 continue;
379
380 addedEngines.append(t: key);
381 if (QBearerEngine *engine = qLoadPlugin<QBearerEngine, QBearerEnginePlugin>(loader: l, key)) {
382 if (key == QLatin1String("generic"))
383 generic = engine;
384 else
385 sessionEngines.append(t: engine);
386
387 engine->moveToThread(thread: bearerThread);
388
389 connect(sender: engine, SIGNAL(updateCompleted()),
390 receiver: this, SLOT(updateConfigurations()),
391 Qt::QueuedConnection);
392 connect(sender: engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)),
393 receiver: this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer)),
394 Qt::QueuedConnection);
395 connect(sender: engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)),
396 receiver: this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer)),
397 Qt::QueuedConnection);
398 connect(sender: engine, SIGNAL(configurationChanged(QNetworkConfigurationPrivatePointer)),
399 receiver: this, SLOT(configurationChanged(QNetworkConfigurationPrivatePointer)),
400 Qt::QueuedConnection);
401 }
402 }
403
404 if (generic) {
405 if (!envOK || skipGeneric <= 0)
406 sessionEngines.append(t: generic);
407 else
408 delete generic;
409 }
410 }
411
412 QBearerEngine *engine = qobject_cast<QBearerEngine *>(object: sender());
413 if (engine && !updatingEngines.isEmpty())
414 updatingEngines.remove(value: engine);
415
416 if (updating && updatingEngines.isEmpty()) {
417 updating = false;
418 emit configurationUpdateComplete();
419 }
420
421 if (engine && !pollingEngines.isEmpty()) {
422 pollingEngines.remove(value: engine);
423 if (pollingEngines.isEmpty())
424 startPolling();
425 }
426
427 if (firstUpdate) {
428 firstUpdate = false;
429 const QList<QBearerEngine*> enginesToInitialize = sessionEngines; //shallow copy the list in case it is modified when we unlock mutex
430 locker.unlock();
431 for (QBearerEngine* engine : enginesToInitialize)
432 QMetaObject::invokeMethod(obj: engine, member: "initialize", type: Qt::BlockingQueuedConnection);
433 }
434}
435
436void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate()
437{
438 const auto locker = qt_scoped_lock(mutex);
439
440 if (sessionEngines.isEmpty()) {
441 emit configurationUpdateComplete();
442 return;
443 }
444
445 updating = true;
446
447 for (QBearerEngine *engine : qAsConst(t&: sessionEngines)) {
448 updatingEngines.insert(value: engine);
449 QMetaObject::invokeMethod(obj: engine, member: "requestUpdate");
450 }
451}
452
453QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const
454{
455 const auto locker = qt_scoped_lock(mutex);
456
457 return sessionEngines;
458}
459
460void QNetworkConfigurationManagerPrivate::startPolling()
461{
462 const auto locker = qt_scoped_lock(mutex);
463 if (!pollTimer) {
464 pollTimer = new QTimer(this);
465 bool ok;
466 int interval = qEnvironmentVariableIntValue(varName: "QT_BEARER_POLL_TIMEOUT", ok: &ok);
467 if (!ok)
468 interval = 10000;//default 10 seconds
469 pollTimer->setInterval(interval);
470 pollTimer->setSingleShot(true);
471 connect(sender: pollTimer, SIGNAL(timeout()), receiver: this, SLOT(pollEngines()));
472 }
473
474 if (pollTimer->isActive())
475 return;
476
477 for (QBearerEngine *engine : qAsConst(t&: sessionEngines)) {
478 if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
479 pollTimer->start();
480 break;
481 }
482 }
483 performAsyncConfigurationUpdate();
484}
485
486void QNetworkConfigurationManagerPrivate::pollEngines()
487{
488 const auto locker = qt_scoped_lock(mutex);
489
490 for (QBearerEngine *engine : qAsConst(t&: sessionEngines)) {
491 if (engine->requiresPolling() && (forcedPolling || engine->configurationsInUse())) {
492 pollingEngines.insert(value: engine);
493 QMetaObject::invokeMethod(obj: engine, member: "requestUpdate");
494 }
495 }
496}
497
498void QNetworkConfigurationManagerPrivate::enablePolling()
499{
500 const auto locker = qt_scoped_lock(mutex);
501
502 ++forcedPolling;
503
504 if (forcedPolling == 1)
505 QMetaObject::invokeMethod(obj: this, member: "startPolling");
506}
507
508void QNetworkConfigurationManagerPrivate::disablePolling()
509{
510 const auto locker = qt_scoped_lock(mutex);
511
512 --forcedPolling;
513}
514
515QT_END_NAMESPACE
516
517#endif // QT_NO_BEARERMANAGEMENT
518

source code of qtbase/src/network/bearer/qnetworkconfigmanager_p.cpp