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 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | QNetworkConfigurationManagerPrivate::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 | |
70 | void 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 | |
82 | QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() |
83 | { |
84 | QMutexLocker locker(&mutex); |
85 | |
86 | qDeleteAll(c: sessionEngines); |
87 | sessionEngines.clear(); |
88 | if (bearerThread) |
89 | bearerThread->quit(); |
90 | } |
91 | |
92 | void QNetworkConfigurationManagerPrivate::cleanup() |
93 | { |
94 | QThread* thread = bearerThread; |
95 | deleteLater(); |
96 | if (thread->wait(deadline: QDeadlineTimer(5000))) |
97 | delete thread; |
98 | } |
99 | |
100 | QNetworkConfiguration 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 | |
207 | QList<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 | |
243 | QNetworkConfiguration 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 | |
268 | bool 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 | |
277 | QNetworkConfigurationManager::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 | |
289 | void 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 | |
309 | void 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 | |
329 | void 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 | |
355 | void 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 | |
436 | void 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 | |
453 | QList<QBearerEngine *> QNetworkConfigurationManagerPrivate::engines() const |
454 | { |
455 | const auto locker = qt_scoped_lock(mutex); |
456 | |
457 | return sessionEngines; |
458 | } |
459 | |
460 | void 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 | |
486 | void 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 | |
498 | void 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 | |
508 | void QNetworkConfigurationManagerPrivate::disablePolling() |
509 | { |
510 | const auto locker = qt_scoped_lock(mutex); |
511 | |
512 | --forcedPolling; |
513 | } |
514 | |
515 | QT_END_NAMESPACE |
516 | |
517 | #endif // QT_NO_BEARERMANAGEMENT |
518 | |