Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qnetworkinformation.cpp
Go to the documentation of this file.
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
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
22Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info");
23
25{
26 void operator()(QNetworkInformation *information) { delete information; }
27};
28
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
57public:
58 QString name() const override { return u"dummy"_s; }
59 QNetworkInformation::Features featuresSupported() const override
60 {
61 return {};
62 }
63};
64
66{
67 Q_DECLARE_PUBLIC(QNetworkInformation)
68public:
72
73 static QNetworkInformation *create(QNetworkInformation::Features features);
77 {
78 if (!dataHolder())
79 return nullptr;
80 QMutexLocker locker(&dataHolder->instanceMutex);
81 return dataHolder->instanceHolder.get();
82 }
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,
114 if (!a || !b)
115 return a && !b;
116 auto aFeaturesSupported = qPopulationCount(unsigned(a->featuresSupported()));
117 auto bFeaturesSupported = qPopulationCount(unsigned(b->featuresSupported()));
118 return aFeaturesSupported > bFeaturesSupported
119 || (aFeaturesSupported == bFeaturesSupported
120 && a->name().compare(b->name(), Qt::CaseInsensitive) < 0);
121 };
122 QMutexLocker instanceLocker(&dataHolder->instanceMutex);
123 std::sort(dataHolder->factories.begin(), dataHolder->factories.end(), featuresNameOrder);
124
125 return !dataHolder->factories.isEmpty();
126}
127
129{
130 // @note: factory is in the base class ctor
131 if (!dataHolder())
132 return;
133 QMutexLocker locker(&dataHolder->instanceMutex);
134 dataHolder->factories.append(factory);
135}
136
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(factory);
144}
145
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
158 result.reserve(copy.size());
159 for (const auto *factory : copy)
160 result << factory->name();
161 return result;
162}
163
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(name, Qt::CaseInsensitive) == 0;
188 };
189 auto it = std::find_if(dataHolder->factories.cbegin(), dataHolder->factories.cend(),
190 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((*it)->featuresSupported());
211 if (!backend)
212 return nullptr;
213 dataHolder->instanceHolder.reset(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(features);
242 };
243
244 for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
245 ++it) {
246 it = std::find_if(it, end, supportsRequestedFeatures);
247 if (it == end) {
248#ifdef DEBUG_LOADING
249 if (dataHolder->factories.isEmpty()) {
250 qDebug("No plugins available");
251 } else {
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(features)) {
266 dataHolder->instanceHolder.reset(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
283{
284 if (!dataHolder())
285 return nullptr;
286
287 QMutexLocker locker(&dataHolder->instanceMutex);
288 if (dataHolder->instanceHolder)
289 return dataHolder->instanceHolder.get();
290
292 dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
293 return dataHolder->instanceHolder.get();
294}
295
315
369
378
506QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
507 : QObject(*(new QNetworkInformationPrivate(backend)))
508{
517
518 QThread *main = nullptr;
519
521 main = QCoreApplication::instance()->thread();
522
523 if (main && thread() != main)
525}
526
531
547{
548 return d_func()->backend->reachability();
549}
550
563{
564 return d_func()->backend->behindCaptivePortal();
565}
566
580{
581 return d_func()->backend->transportMedium();
582}
583
596{
597 return d_func()->backend->isMetered();
598}
599
604{
605 return d_func()->backend->name();
606}
607
612bool QNetworkInformation::supports(Features features) const
613{
614 return (d_func()->backend->featuresSupported() & features) == features;
615}
616
622QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
623{
624 return d_func()->backend->featuresSupported();
625}
626
670{
671 int index = -1;
672#ifdef Q_OS_WIN
674#elif defined(Q_OS_DARWIN)
676#elif defined(Q_OS_ANDROID)
678#elif defined(Q_OS_LINUX)
680#endif
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:
686 return true;
687
688 // Fall back to the dummy backend
689 return loadBackendByName(u"dummy");
690}
691
704{
705 if (backend == u"dummy")
706 return QNetworkInformationPrivate::createDummy() != nullptr;
707
708 auto loadedBackend = QNetworkInformationPrivate::create(backend);
709 return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0;
710}
711
712#if QT_DEPRECATED_SINCE(6,4)
718bool QNetworkInformation::load(QStringView backend)
719{
720 return loadBackendByName(backend);
721}
722#endif // QT_DEPRECATED_SINCE(6,4)
723
734{
735 auto loadedBackend = QNetworkInformationPrivate::create(features);
736 return loadedBackend && loadedBackend->supports(features);
737}
738
739#if QT_DEPRECATED_SINCE(6,4)
745bool QNetworkInformation::load(Features features)
746{
747 return loadBackendByFeatures(features);
748}
749#endif // QT_DEPRECATED_SINCE(6,4)
750
758
769
771
772#include "moc_qnetworkinformation.cpp"
773#include "moc_qnetworkinformation_p.cpp"
774#include "qnetworkinformation.moc"
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
Definition qlist.h:75
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:319
\inmodule QtCore
Definition qmutex.h:281
QNetworkInformationBackendFactory provides the interface for creating instances of QNetworkInformatio...
QNetworkInformationBackend provides the interface with which QNetworkInformation does all of its actu...
static constexpr int PluginNamesLinuxIndex
static constexpr int PluginNamesAppleIndex
void transportMediumChanged(TransportMedium medium)
static constexpr int PluginNamesWindowsIndex
~QNetworkInformationBackend() override
void isMeteredChanged(bool isMetered)
virtual QNetworkInformation::Features featuresSupported() const =0
Features supported, return the same in QNetworkInformationBackendFactory::featuresSupported().
void behindCaptivePortalChanged(bool behindPortal)
static constexpr int PluginNamesAndroidIndex
static const char16_t PluginNames[4][22]
void reachabilityChanged(Reachability reachability)
You should not emit this signal manually, call setReachability() instead which will emit this signal ...
QString name() const override
Backend name, return the same in QNetworkInformationBackendFactory::name().
QNetworkInformation::Features featuresSupported() const override
Features supported, return the same in QNetworkInformationBackendFactory::featuresSupported().
static QNetworkInformation * createDummy()
QNetworkInformationPrivate(QNetworkInformationBackend *backend)
static void addToList(QNetworkInformationBackendFactory *factory)
static QNetworkInformation * instance()
static QNetworkInformation * create(QNetworkInformation::Features features)
static void removeFromList(QNetworkInformationBackendFactory *factory)
\inmodule QtNetwork
void transportMediumChanged(TransportMedium current)
static bool loadBackendByFeatures(Features features)
QString backendName() const
Returns the name of the currently loaded backend.
bool isMetered
Check if the current connection is metered.
void isMeteredChanged(bool isMetered)
void reachabilityChanged(Reachability newReachability)
bool isBehindCaptivePortal
Lets you know if the user's device is behind a captive portal.
Reachability reachability
The current state of the system's network connectivity.
TransportMedium transportMedium
The currently active transport medium for the application.
Features supportedFeatures() const
static bool loadBackendByName(QStringView backend)
bool supports(Features features) const
Returns true if the currently loaded backend supports features.
static QStringList availableBackends()
Returns a list of the names of all currently available backends.
Reachability
\value Unknown If this value is returned then we may be connected but the OS has still not confirmed ...
void isBehindCaptivePortalChanged(bool state)
static QNetworkInformation * instance()
Returns a pointer to the instance of the QNetworkInformation, if any.
~QNetworkInformation() override
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL)
Changes the thread affinity for this object and its children and returns true on success.
Definition qobject.cpp:1643
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1325
int main()
[0]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ CaseInsensitive
Q_DECL_CONST_FUNCTION QT_POPCOUNT_CONSTEXPR uint qPopulationCount(quint32 v) noexcept
static jboolean copy(JNIEnv *, jobject)
void qAddPostRoutine(QtCleanUpFunction p)
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qDebug
[1]
Definition qlogging.h:164
#define Q_LOGGING_CATEGORY(name,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
static void networkInfoCleanup()
#define QNetworkInformationBackendFactory_iid
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLuint GLuint end
GLuint name
GLuint GLuint * names
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
#define Q_OBJECT
QMutex mutex
[2]
QItemEditorFactory * factory
QHostInfo info
[0]
view create()
void operator()(QNetworkInformation *information)