1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4// #define DEBUG_LOADING
5
6#include "qnetworkinformation.h"
7#include <QtNetwork/private/qnetworkinformation_p.h>
8#include <QtNetwork/qnetworkinformation.h>
9
10#include <QtCore/private/qobject_p.h>
11#include <QtCore/qcoreapplication.h>
12#include <QtCore/qmutex.h>
13#include <QtCore/qthread.h>
14#include <QtCore/private/qfactoryloader_p.h>
15
16#include <algorithm>
17#include <memory>
18#include <mutex>
19
20QT_BEGIN_NAMESPACE
21Q_DECLARE_LOGGING_CATEGORY(lcNetInfo)
22Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info");
23
24struct QNetworkInformationDeleter
25{
26 void operator()(QNetworkInformation *information) { delete information; }
27};
28
29Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader,
30 (QNetworkInformationBackendFactory_iid,
31 QStringLiteral("/networkinformation")))
32
33struct QStaticNetworkInformationDataHolder
34{
35 QMutex instanceMutex;
36 std::unique_ptr<QNetworkInformation, QNetworkInformationDeleter> instanceHolder;
37 QList<QNetworkInformationBackendFactory *> factories;
38};
39Q_GLOBAL_STATIC(QStaticNetworkInformationDataHolder, dataHolder);
40
41static void networkInfoCleanup()
42{
43 if (!dataHolder.exists())
44 return;
45 QMutexLocker locker(&dataHolder->instanceMutex);
46 QNetworkInformation *instance = dataHolder->instanceHolder.get();
47 if (!instance)
48 return;
49
50 dataHolder->instanceHolder.reset();
51}
52
53using namespace Qt::Literals::StringLiterals;
54
55class QNetworkInformationDummyBackend : public QNetworkInformationBackend {
56 Q_OBJECT
57public:
58 QString name() const override { return u"dummy"_s; }
59 QNetworkInformation::Features featuresSupported() const override
60 {
61 return {};
62 }
63};
64
65class QNetworkInformationPrivate : public QObjectPrivate
66{
67 Q_DECLARE_PUBLIC(QNetworkInformation)
68public:
69 QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) {
70 qAddPostRoutine(&networkInfoCleanup);
71 }
72
73 static QNetworkInformation *create(QNetworkInformation::Features features);
74 static QNetworkInformation *create(QStringView name);
75 static QNetworkInformation *createDummy();
76 static QNetworkInformation *instance()
77 {
78 if (!dataHolder())
79 return nullptr;
80 QMutexLocker locker(&dataHolder->instanceMutex);
81 return dataHolder->instanceHolder.get();
82 }
83 static QStringList backendNames();
84 static void addToList(QNetworkInformationBackendFactory *factory);
85 static void removeFromList(QNetworkInformationBackendFactory *factory);
86
87private:
88 static bool initializeList();
89
90 std::unique_ptr<QNetworkInformationBackend> backend;
91};
92
93bool QNetworkInformationPrivate::initializeList()
94{
95 if (!qniLoader())
96 return false;
97 if (!dataHolder())
98 return false;
99 Q_CONSTINIT static QBasicMutex mutex;
100 QMutexLocker initLocker(&mutex);
101
102#if QT_CONFIG(library)
103 qniLoader->update();
104#endif
105 // Instantiates the plugins (and registers the factories)
106 int index = 0;
107 while (qniLoader->instance(index))
108 ++index;
109 initLocker.unlock();
110
111 // Now sort the list on number of features available (then name)
112 const auto featuresNameOrder = [](QNetworkInformationBackendFactory *a,
113 QNetworkInformationBackendFactory *b) {
114 if (!a || !b)
115 return a && !b;
116 auto aFeaturesSupported = qPopulationCount(v: unsigned(a->featuresSupported()));
117 auto bFeaturesSupported = qPopulationCount(v: unsigned(b->featuresSupported()));
118 return aFeaturesSupported > bFeaturesSupported
119 || (aFeaturesSupported == bFeaturesSupported
120 && a->name().compare(s: b->name(), cs: Qt::CaseInsensitive) < 0);
121 };
122 QMutexLocker instanceLocker(&dataHolder->instanceMutex);
123 std::sort(first: dataHolder->factories.begin(), last: dataHolder->factories.end(), comp: featuresNameOrder);
124
125 return !dataHolder->factories.isEmpty();
126}
127
128void QNetworkInformationPrivate::addToList(QNetworkInformationBackendFactory *factory)
129{
130 // @note: factory is in the base class ctor
131 if (!dataHolder())
132 return;
133 QMutexLocker locker(&dataHolder->instanceMutex);
134 dataHolder->factories.append(t: factory);
135}
136
137void QNetworkInformationPrivate::removeFromList(QNetworkInformationBackendFactory *factory)
138{
139 // @note: factory is in the base class dtor
140 if (!dataHolder.exists())
141 return;
142 QMutexLocker locker(&dataHolder->instanceMutex);
143 dataHolder->factories.removeAll(t: factory);
144}
145
146QStringList QNetworkInformationPrivate::backendNames()
147{
148 if (!dataHolder())
149 return {};
150 if (!initializeList())
151 return {};
152
153 QMutexLocker locker(&dataHolder->instanceMutex);
154 const QList copy = dataHolder->factories;
155 locker.unlock();
156
157 QStringList result;
158 result.reserve(asize: copy.size());
159 for (const auto *factory : copy)
160 result << factory->name();
161 return result;
162}
163
164QNetworkInformation *QNetworkInformationPrivate::create(QStringView name)
165{
166 if (name.isEmpty())
167 return nullptr;
168 if (!dataHolder())
169 return nullptr;
170#ifdef DEBUG_LOADING
171 qDebug().nospace() << "create() called with name=\"" << name
172 << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
173#endif
174 if (!initializeList()) {
175#ifdef DEBUG_LOADING
176 qDebug("Failed to initialize list, returning.");
177#endif
178 return nullptr;
179 }
180
181 QMutexLocker locker(&dataHolder->instanceMutex);
182 if (dataHolder->instanceHolder)
183 return dataHolder->instanceHolder.get();
184
185
186 const auto nameMatches = [name](QNetworkInformationBackendFactory *factory) {
187 return factory->name().compare(s: name, cs: Qt::CaseInsensitive) == 0;
188 };
189 auto it = std::find_if(first: dataHolder->factories.cbegin(), last: dataHolder->factories.cend(),
190 pred: nameMatches);
191 if (it == dataHolder->factories.cend()) {
192#ifdef DEBUG_LOADING
193 if (dataHolder->factories.isEmpty()) {
194 qDebug("No plugins available");
195 } else {
196 QString listNames;
197 listNames.reserve(8 * dataHolder->factories.count());
198 for (const auto *factory : std::as_const(dataHolder->factories))
199 listNames += factory->name() + ", "_L1;
200 listNames.chop(2);
201 qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
202 << listNames << " }";
203 }
204#endif
205 return nullptr;
206 }
207#ifdef DEBUG_LOADING
208 qDebug() << "Creating instance using loader named " << (*it)->name();
209#endif
210 QNetworkInformationBackend *backend = (*it)->create(requiredFeatures: (*it)->featuresSupported());
211 if (!backend)
212 return nullptr;
213 dataHolder->instanceHolder.reset(p: new QNetworkInformation(backend));
214 Q_ASSERT(name.isEmpty()
215 || dataHolder->instanceHolder->backendName().compare(name, Qt::CaseInsensitive) == 0);
216 return dataHolder->instanceHolder.get();
217}
218
219QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Features features)
220{
221 if (!dataHolder())
222 return nullptr;
223#ifdef DEBUG_LOADING
224 qDebug().nospace() << "create() called with features=\"" << features
225 << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
226#endif
227 if (features == 0)
228 return nullptr;
229
230 if (!initializeList()) {
231#ifdef DEBUG_LOADING
232 qDebug("Failed to initialize list, returning.");
233#endif
234 return nullptr;
235 }
236 QMutexLocker locker(&dataHolder->instanceMutex);
237 if (dataHolder->instanceHolder)
238 return dataHolder->instanceHolder.get();
239
240 const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
241 return factory && factory->featuresSupported().testFlags(flags: features);
242 };
243
244 for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
245 ++it) {
246 it = std::find_if(first: it, last: end, pred: supportsRequestedFeatures);
247 if (it == end) {
248#ifdef DEBUG_LOADING
249 if (dataHolder->factories.isEmpty()) {
250 qDebug("No plugins available");
251 } else {
252 QStringList names;
253 names.reserve(dataHolder->factories.count());
254 for (const auto *factory : std::as_const(dataHolder->factories))
255 names += factory->name();
256 qDebug() << "None of the following backends has all the requested features:"
257 << names << features;
258 }
259#endif
260 break;
261 }
262#ifdef DEBUG_LOADING
263 qDebug() << "Creating instance using loader named" << (*it)->name();
264#endif
265 if (QNetworkInformationBackend *backend = (*it)->create(requiredFeatures: features)) {
266 dataHolder->instanceHolder.reset(p: new QNetworkInformation(backend));
267 Q_ASSERT(dataHolder->instanceHolder->supports(features));
268 return dataHolder->instanceHolder.get();
269 }
270#ifdef DEBUG_LOADING
271 else {
272 qDebug() << "The factory returned a nullptr";
273 }
274#endif
275 }
276#ifdef DEBUG_LOADING
277 qDebug() << "Couldn't find/create an appropriate backend.";
278#endif
279 return nullptr;
280}
281
282QNetworkInformation *QNetworkInformationPrivate::createDummy()
283{
284 if (!dataHolder())
285 return nullptr;
286
287 QMutexLocker locker(&dataHolder->instanceMutex);
288 if (dataHolder->instanceHolder)
289 return dataHolder->instanceHolder.get();
290
291 QNetworkInformationBackend *backend = new QNetworkInformationDummyBackend;
292 dataHolder->instanceHolder.reset(p: new QNetworkInformation(backend));
293 return dataHolder->instanceHolder.get();
294}
295
296/*!
297 \class QNetworkInformationBackend
298 \internal (Semi-private)
299 \brief QNetworkInformationBackend provides the interface with
300 which QNetworkInformation does all of its actual work.
301
302 Deriving from and implementing this class makes it a candidate
303 for use with QNetworkInformation. The derived class must, on
304 updates, call setters in the QNetworkInformationBackend which
305 will update the values and emit signals if the value has changed.
306
307 \sa QNetworkInformationBackendFactory
308*/
309
310/*!
311 \internal
312 Destroys base backend class.
313*/
314QNetworkInformationBackend::~QNetworkInformationBackend() = default;
315
316/*!
317 \fn QNetworkInformationBackend::name()
318
319 Backend name, return the same in
320 QNetworkInformationBackendFactory::name().
321*/
322
323/*!
324 \fn QNetworkInformation::Features QNetworkInformationBackend::featuresSupported()
325
326 Features supported, return the same in
327 QNetworkInformationBackendFactory::featuresSupported().
328*/
329
330/*!
331 \fn void QNetworkInformationBackend::reachabilityChanged(NetworkInformation::Reachability reachability)
332
333 You should not emit this signal manually, call setReachability()
334 instead which will emit this signal when the value changes.
335
336 \sa setReachability
337*/
338
339/*!
340 \fn void QNetworkInformationBackend::setReachability(QNetworkInformation::Reachability reachability)
341
342 Call this when reachability has changed. It will automatically
343 emit reachabilityChanged().
344
345 \sa setReachability
346*/
347
348/*!
349 \class QNetworkInformationBackendFactory
350 \internal (Semi-private)
351 \brief QNetworkInformationBackendFactory provides the interface
352 for creating instances of QNetworkInformationBackend.
353
354 Deriving from and implementing this class will let you register
355 your plugin with QNetworkInformation. It must provide some basic
356 information for querying information about the backend, and must
357 also create the backend if requested. If some pre-conditions for
358 the backend is not met it must return \nullptr.
359*/
360
361/*!
362 \internal
363 Adds the factory to an internal list.
364*/
365QNetworkInformationBackendFactory::QNetworkInformationBackendFactory()
366{
367 QNetworkInformationPrivate::addToList(factory: this);
368}
369
370/*!
371 \internal
372 Removes the factory from an internal list.
373*/
374QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory()
375{
376 QNetworkInformationPrivate::removeFromList(factory: this);
377}
378
379/*!
380 \fn QString QNetworkInformationBackendFactory::name()
381
382 Backend name, return the same in
383 QNetworkInformationBackend::name().
384*/
385
386/*!
387 \fn QNetworkInformation::Features QNetworkInformationBackendFactory::featuresSupported()
388
389 Features supported, return the same in
390 QNetworkInformationBackend::featuresSupported().
391 The factory should not promise support for features that wouldn't
392 be available after creating the backend.
393*/
394
395/*!
396 \fn QNetworkInformationBackend *QNetworkInformationBackendFactory::create()
397
398 Create and return an instance of QNetworkInformationBackend. It
399 will be deallocated by QNetworkInformation on shutdown. If some
400 precondition is not met, meaning the backend would not function
401 correctly, then you must return \nullptr.
402*/
403
404/*!
405 \class QNetworkInformation
406 \inmodule QtNetwork
407 \since 6.1
408 \brief QNetworkInformation exposes various network information
409 through native backends.
410
411 QNetworkInformation provides a cross-platform interface to
412 network-related information through plugins.
413
414 Various plugins can have various functionality supported, and so
415 you can load() plugins based on which features are needed.
416
417 QNetworkInformation is a singleton and stays alive from the first
418 successful load() until destruction of the QCoreApplication object.
419 If you destroy and re-create the QCoreApplication object you must call
420 load() again.
421
422 \sa QNetworkInformation::Feature
423*/
424
425/*!
426 \enum QNetworkInformation::Feature
427
428 Lists all of the features that a plugin may currently support.
429 This can be used in QNetworkInformation::load().
430
431 \value Reachability
432 If the plugin supports this feature then the \c reachability property
433 will provide useful results. Otherwise it will always return
434 \c{Reachability::Unknown}.
435 See also QNetworkInformation::Reachability.
436
437 \value CaptivePortal
438 If the plugin supports this feature then the \c isBehindCaptivePortal
439 property will provide useful results. Otherwise it will always return
440 \c{false}.
441
442 \value TransportMedium
443 If the plugin supports this feature then the \c transportMedium
444 property will provide useful results. Otherwise it will always return
445 \c{TransportMedium::Unknown}.
446 See also QNetworkInformation::TransportMedium.
447
448 \value Metered
449 If the plugin supports this feature then the \c isMetered
450 property will provide useful results. Otherwise it will always return
451 \c{false}.
452*/
453
454/*!
455 \enum QNetworkInformation::Reachability
456
457 \value Unknown
458 If this value is returned then we may be connected but the OS
459 has still not confirmed full connectivity, or this feature
460 is not supported.
461 \value Disconnected
462 Indicates that the system may have no connectivity at all.
463 \value Local
464 Indicates that the system is connected to a network, but it
465 might only be able to access devices on the local network.
466 \value Site
467 Indicates that the system is connected to a network, but it
468 might only be able to access devices on the local subnet or an
469 intranet.
470 \value Online
471 Indicates that the system is connected to a network and
472 able to access the Internet.
473
474 \sa QNetworkInformation::reachability
475*/
476
477/*!
478 \enum QNetworkInformation::TransportMedium
479 \since 6.3
480
481 Lists the currently recognized media with which one can connect to the
482 internet.
483
484 \value Unknown
485 Returned if either the OS reports no active medium, the active medium is
486 not recognized by Qt, or the TransportMedium feature is not supported.
487 \value Ethernet
488 Indicates that the currently active connection is using ethernet.
489 Note: This value may also be returned when Windows is connected to a
490 Bluetooth personal area network.
491 \value Cellular
492 Indicates that the currently active connection is using a cellular
493 network.
494 \value WiFi
495 Indicates that the currently active connection is using Wi-Fi.
496 \value Bluetooth
497 Indicates that the currently active connection is connected using
498 Bluetooth.
499
500 \sa QNetworkInformation::transportMedium
501*/
502
503/*!
504 \internal ctor
505*/
506QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
507 : QObject(*(new QNetworkInformationPrivate(backend)))
508{
509 connect(sender: backend, signal: &QNetworkInformationBackend::reachabilityChanged, context: this,
510 slot: &QNetworkInformation::reachabilityChanged);
511 connect(sender: backend, signal: &QNetworkInformationBackend::behindCaptivePortalChanged, context: this,
512 slot: &QNetworkInformation::isBehindCaptivePortalChanged);
513 connect(sender: backend, signal: &QNetworkInformationBackend::transportMediumChanged, context: this,
514 slot: &QNetworkInformation::transportMediumChanged);
515 connect(sender: backend, signal: &QNetworkInformationBackend::isMeteredChanged, context: this,
516 slot: &QNetworkInformation::isMeteredChanged);
517
518 QThread *main = nullptr;
519
520 if (QCoreApplication::instance())
521 main = QCoreApplication::instance()->thread();
522
523 if (main && thread() != main)
524 moveToThread(thread: main);
525}
526
527/*!
528 \internal dtor
529*/
530QNetworkInformation::~QNetworkInformation() = default;
531
532/*!
533 \property QNetworkInformation::reachability
534 \brief The current state of the system's network connectivity.
535
536 Indicates the level of connectivity that can be expected. Do note
537 that this is only based on what the plugin/operating system
538 reports. In certain scenarios this is known to be wrong. For
539 example, on Windows the 'Online' check, by default, is performed
540 by Windows connecting to a Microsoft-owned server. If this server
541 is for any reason blocked then it will assume it does not have
542 Online reachability. Because of this you should not use this as a
543 pre-check before attempting to make a connection.
544*/
545
546QNetworkInformation::Reachability QNetworkInformation::reachability() const
547{
548 return d_func()->backend->reachability();
549}
550
551/*!
552 \property QNetworkInformation::isBehindCaptivePortal
553 \brief Lets you know if the user's device is behind a captive portal.
554 \since 6.2
555
556 This property indicates if the user's device is currently known to be
557 behind a captive portal. This functionality relies on the operating system's
558 detection of captive portals and is not supported on systems that don't
559 report this. On systems where this is not supported this will always return
560 \c{false}.
561*/
562bool QNetworkInformation::isBehindCaptivePortal() const
563{
564 return d_func()->backend->behindCaptivePortal();
565}
566
567/*!
568 \property QNetworkInformation::transportMedium
569 \brief The currently active transport medium for the application
570 \since 6.3
571
572 This property returns the currently active transport medium for the
573 application, on operating systems where such information is available.
574
575 When the current transport medium changes a signal is emitted, this can,
576 for instance, occur when a user leaves the range of a WiFi network, unplugs
577 their ethernet cable or enables Airplane mode.
578*/
579QNetworkInformation::TransportMedium QNetworkInformation::transportMedium() const
580{
581 return d_func()->backend->transportMedium();
582}
583
584/*!
585 \property QNetworkInformation::isMetered
586 \brief Check if the current connection is metered
587 \since 6.3
588
589 This property returns whether the current connection is (known to be)
590 metered or not. You can use this as a guiding factor to decide whether your
591 application should perform certain network requests or uploads.
592 For instance, you may not want to upload logs or diagnostics while this
593 property is \c true.
594*/
595bool QNetworkInformation::isMetered() const
596{
597 return d_func()->backend->isMetered();
598}
599
600/*!
601 Returns the name of the currently loaded backend.
602*/
603QString QNetworkInformation::backendName() const
604{
605 return d_func()->backend->name();
606}
607
608/*!
609 Returns \c true if the currently loaded backend supports
610 \a features.
611*/
612bool QNetworkInformation::supports(Features features) const
613{
614 return (d_func()->backend->featuresSupported() & features) == features;
615}
616
617/*!
618 \since 6.3
619
620 Returns all the supported features of the current backend.
621*/
622QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
623{
624 return d_func()->backend->featuresSupported();
625}
626
627/*!
628 \since 6.3
629
630 Attempts to load the platform-default backend.
631
632 \note Starting with 6.7 this tries to load any backend that supports
633 \l{QNetworkInformation::Feature::Reachability}{Reachability} if the
634 platform-default backend is not available or fails to load.
635 If this also fails it will fall back to a backend that only returns
636 the default values for all properties.
637
638 This platform-to-plugin mapping is as follows:
639
640 \table
641 \header
642 \li Platform
643 \li Plugin-name
644 \row
645 \li Windows
646 \li networklistmanager
647 \row
648 \li Apple (macOS/iOS)
649 \li scnetworkreachability
650 \row
651 \li Android
652 \li android
653 \row
654 \li Linux
655 \li networkmanager
656 \endtable
657
658 This function is provided for convenience where the logic earlier
659 is good enough. If you require a specific plugin then you should call
660 loadBackendByName() or loadBackendByFeatures() directly instead.
661
662 Determines a suitable backend to load and returns \c true if this backend
663 is already loaded or on successful loading of it. Returns \c false if any
664 other backend has already been loaded, or if loading of the selected
665 backend fails.
666
667 \sa instance(), load()
668*/
669bool QNetworkInformation::loadDefaultBackend()
670{
671 int index = -1;
672#ifdef Q_OS_WIN
673 index = QNetworkInformationBackend::PluginNamesWindowsIndex;
674#elif defined(Q_OS_DARWIN)
675 index = QNetworkInformationBackend::PluginNamesAppleIndex;
676#elif defined(Q_OS_ANDROID)
677 index = QNetworkInformationBackend::PluginNamesAndroidIndex;
678#elif defined(Q_OS_LINUX)
679 index = QNetworkInformationBackend::PluginNamesLinuxIndex;
680#endif
681 if (index != -1 && loadBackendByName(backend: QNetworkInformationBackend::PluginNames[index]))
682 return true;
683 // We assume reachability is the most commonly wanted feature, and try to
684 // load the backend that advertises the most features including that:
685 if (loadBackendByFeatures(features: Feature::Reachability))
686 return true;
687
688 // Fall back to the dummy backend
689 return loadBackendByName(backend: u"dummy");
690}
691
692/*!
693 \since 6.4
694
695 Attempts to load a backend whose name matches \a backend
696 (case insensitively).
697
698 Returns \c true if it managed to load the requested backend or
699 if it was already loaded. Returns \c false otherwise.
700
701 \sa instance
702*/
703bool QNetworkInformation::loadBackendByName(QStringView backend)
704{
705 if (backend == u"dummy")
706 return QNetworkInformationPrivate::createDummy() != nullptr;
707
708 auto loadedBackend = QNetworkInformationPrivate::create(name: backend);
709 return loadedBackend && loadedBackend->backendName().compare(s: backend, cs: Qt::CaseInsensitive) == 0;
710}
711
712#if QT_DEPRECATED_SINCE(6,4)
713/*!
714 \deprecated [6.4] Use loadBackendByName() instead.
715
716 \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures()
717*/
718bool QNetworkInformation::load(QStringView backend)
719{
720 return loadBackendByName(backend);
721}
722#endif // QT_DEPRECATED_SINCE(6,4)
723
724/*!
725 \since 6.4
726 Load a backend which supports \a features.
727
728 Returns \c true if it managed to load the requested backend or
729 if it was already loaded. Returns \c false otherwise.
730
731 \sa instance
732*/
733bool QNetworkInformation::loadBackendByFeatures(Features features)
734{
735 auto loadedBackend = QNetworkInformationPrivate::create(features);
736 return loadedBackend && loadedBackend->supports(features);
737}
738
739#if QT_DEPRECATED_SINCE(6,4)
740/*!
741 \deprecated [6.4] Use loadBackendByFeatures() instead.
742
743 \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures()
744*/
745bool QNetworkInformation::load(Features features)
746{
747 return loadBackendByFeatures(features);
748}
749#endif // QT_DEPRECATED_SINCE(6,4)
750
751/*!
752 Returns a list of the names of all currently available backends.
753*/
754QStringList QNetworkInformation::availableBackends()
755{
756 return QNetworkInformationPrivate::backendNames();
757}
758
759/*!
760 Returns a pointer to the instance of the QNetworkInformation,
761 if any.
762
763 \sa load()
764*/
765QNetworkInformation *QNetworkInformation::instance()
766{
767 return QNetworkInformationPrivate::instance();
768}
769
770QT_END_NAMESPACE
771
772#include "moc_qnetworkinformation.cpp"
773#include "moc_qnetworkinformation_p.cpp"
774#include "qnetworkinformation.moc"
775

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/network/kernel/qnetworkinformation.cpp