Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
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;
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 auto needsReinvoke = instance->thread() && instance->thread() != QThread::currentThread();
51 if (needsReinvoke) {
52 QMetaObject::invokeMethod(dataHolder->instanceHolder.get(), []() { networkInfoCleanup(); });
53 return;
54 }
55 dataHolder->instanceHolder.reset();
56}
57
59{
60 Q_DECLARE_PUBLIC(QNetworkInformation)
61public:
64 }
65
66 static QNetworkInformation *create(QNetworkInformation::Features features);
69 {
70 if (!dataHolder())
71 return nullptr;
72 QMutexLocker locker(&dataHolder->instanceMutex);
73 return dataHolder->instanceHolder.get();
74 }
78
79private:
80 static bool initializeList();
81
82 std::unique_ptr<QNetworkInformationBackend> backend;
83};
84
85bool QNetworkInformationPrivate::initializeList()
86{
87 if (!qniLoader())
88 return false;
89 if (!dataHolder())
90 return false;
91 Q_CONSTINIT static QBasicMutex mutex;
92 QMutexLocker initLocker(&mutex);
93
94#if QT_CONFIG(library)
95 qniLoader->update();
96#endif
97 // Instantiates the plugins (and registers the factories)
98 int index = 0;
99 while (qniLoader->instance(index))
100 ++index;
101 initLocker.unlock();
102
103 // Now sort the list on number of features available (then name)
104 const auto featuresNameOrder = [](QNetworkInformationBackendFactory *a,
106 if (!a || !b)
107 return a && !b;
108 auto aFeaturesSupported = qPopulationCount(unsigned(a->featuresSupported()));
109 auto bFeaturesSupported = qPopulationCount(unsigned(b->featuresSupported()));
110 return aFeaturesSupported > bFeaturesSupported
111 || (aFeaturesSupported == bFeaturesSupported
112 && a->name().compare(b->name(), Qt::CaseInsensitive) < 0);
113 };
114 QMutexLocker instanceLocker(&dataHolder->instanceMutex);
115 std::sort(dataHolder->factories.begin(), dataHolder->factories.end(), featuresNameOrder);
116
117 return !dataHolder->factories.isEmpty();
118}
119
121{
122 // @note: factory is in the base class ctor
123 if (!dataHolder())
124 return;
125 QMutexLocker locker(&dataHolder->instanceMutex);
126 dataHolder->factories.append(factory);
127}
128
130{
131 // @note: factory is in the base class dtor
132 if (!dataHolder.exists())
133 return;
134 QMutexLocker locker(&dataHolder->instanceMutex);
135 dataHolder->factories.removeAll(factory);
136}
137
139{
140 if (!dataHolder())
141 return {};
142 if (!initializeList())
143 return {};
144
145 QMutexLocker locker(&dataHolder->instanceMutex);
146 const QList copy = dataHolder->factories;
147 locker.unlock();
148
150 result.reserve(copy.size());
151 for (const auto *factory : copy)
152 result << factory->name();
153 return result;
154}
155
157{
158 if (name.isEmpty())
159 return nullptr;
160 if (!dataHolder())
161 return nullptr;
162#ifdef DEBUG_LOADING
163 qDebug().nospace() << "create() called with name=\"" << name
164 << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
165#endif
166 if (!initializeList()) {
167#ifdef DEBUG_LOADING
168 qDebug("Failed to initialize list, returning.");
169#endif
170 return nullptr;
171 }
172
173 QMutexLocker locker(&dataHolder->instanceMutex);
174 if (dataHolder->instanceHolder)
175 return dataHolder->instanceHolder.get();
176
177
178 const auto nameMatches = [name](QNetworkInformationBackendFactory *factory) {
179 return factory->name().compare(name, Qt::CaseInsensitive) == 0;
180 };
181 auto it = std::find_if(dataHolder->factories.cbegin(), dataHolder->factories.cend(),
182 nameMatches);
183 if (it == dataHolder->factories.cend()) {
184#ifdef DEBUG_LOADING
185 if (dataHolder->factories.isEmpty()) {
186 qDebug("No plugins available");
187 } else {
188 QString listNames;
189 listNames.reserve(8 * dataHolder->factories.count());
190 for (const auto *factory : std::as_const(dataHolder->factories))
191 listNames += factory->name() + QStringLiteral(", ");
192 listNames.chop(2);
193 qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
194 << listNames << " }";
195 }
196#endif
197 return nullptr;
198 }
199#ifdef DEBUG_LOADING
200 qDebug() << "Creating instance using loader named " << (*it)->name();
201#endif
202 QNetworkInformationBackend *backend = (*it)->create((*it)->featuresSupported());
203 if (!backend)
204 return nullptr;
205 dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
206 Q_ASSERT(name.isEmpty()
207 || dataHolder->instanceHolder->backendName().compare(name, Qt::CaseInsensitive) == 0);
208 return dataHolder->instanceHolder.get();
209}
210
211QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Features features)
212{
213 if (!dataHolder())
214 return nullptr;
215#ifdef DEBUG_LOADING
216 qDebug().nospace() << "create() called with features=\"" << features
217 << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
218#endif
219 if (features == 0)
220 return nullptr;
221
222 if (!initializeList()) {
223#ifdef DEBUG_LOADING
224 qDebug("Failed to initialize list, returning.");
225#endif
226 return nullptr;
227 }
228 QMutexLocker locker(&dataHolder->instanceMutex);
229 if (dataHolder->instanceHolder)
230 return dataHolder->instanceHolder.get();
231
232 const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
233 return factory && (factory->featuresSupported() & features) == features;
234 };
235
236 for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
237 ++it) {
238 it = std::find_if(it, end, supportsRequestedFeatures);
239 if (it == end) {
240#ifdef DEBUG_LOADING
241 if (dataHolder->factories.isEmpty()) {
242 qDebug("No plugins available");
243 } else {
245 names.reserve(dataHolder->factories.count());
246 for (const auto *factory : std::as_const(dataHolder->factories))
247 names += factory->name();
248 qDebug() << "None of the following backends has all the requested features:"
249 << names << features;
250 }
251#endif
252 break;
253 }
254#ifdef DEBUG_LOADING
255 qDebug() << "Creating instance using loader named" << (*it)->name();
256#endif
257 if (QNetworkInformationBackend *backend = (*it)->create(features)) {
258 dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
259 Q_ASSERT(dataHolder->instanceHolder->supports(features));
260 return dataHolder->instanceHolder.get();
261 }
262#ifdef DEBUG_LOADING
263 else {
264 qDebug() << "The factory returned a nullptr";
265 }
266#endif
267 }
268#ifdef DEBUG_LOADING
269 qDebug() << "Couldn't find/create an appropriate backend.";
270#endif
271 return nullptr;
272}
273
293
344{
346}
347
353{
355}
356
484QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
485 : QObject(*(new QNetworkInformationPrivate(backend)))
486{
495}
496
501
517{
518 return d_func()->backend->reachability();
519}
520
533{
534 return d_func()->backend->behindCaptivePortal();
535}
536
550{
551 return d_func()->backend->transportMedium();
552}
553
566{
567 return d_func()->backend->isMetered();
568}
569
574{
575 return d_func()->backend->name();
576}
577
582bool QNetworkInformation::supports(Features features) const
583{
584 return (d_func()->backend->featuresSupported() & features) == features;
585}
586
592QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
593{
594 return d_func()->backend->featuresSupported();
595}
596
632{
633 int index = -1;
634#ifdef Q_OS_WIN
636#elif defined(Q_OS_DARWIN)
638#elif defined(Q_OS_ANDROID)
640#elif defined(Q_OS_LINUX)
642#endif
643 if (index == -1)
644 return false;
646}
647
660{
661 auto loadedBackend = QNetworkInformationPrivate::create(backend);
662 return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0;
663}
664
665#if QT_DEPRECATED_SINCE(6,4)
671bool QNetworkInformation::load(QStringView backend)
672{
673 return loadBackendByName(backend);
674}
675#endif // QT_DEPRECATED_SINCE(6,4)
676
687{
688 auto loadedBackend = QNetworkInformationPrivate::create(features);
689 return loadedBackend && loadedBackend->supports(features);
690}
691
692#if QT_DEPRECATED_SINCE(6,4)
698bool QNetworkInformation::load(Features features)
699{
700 return loadBackendByFeatures(features);
701}
702#endif // QT_DEPRECATED_SINCE(6,4)
703
708{
710}
711
719{
721}
722
724
725#include "moc_qnetworkinformation.cpp"
726#include "moc_qnetworkinformation_p.cpp"
Definition qlist.h:74
\inmodule QtCore
Definition qmutex.h:317
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:323
\inmodule QtCore
Definition qmutex.h:285
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)
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 ...
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:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1561
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:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
void reserve(qsizetype size)
Ensures the string has space for at least size characters.
Definition qstring.h:1173
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
static QThread * currentThread()
Definition qthread.cpp:966
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:160
#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]
QT_BEGIN_NAMESPACE struct QPlatformMediaIntegration::InstanceHolder instanceHolder
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
QFileInfo info(fileName)
[8]
QMutex mutex
[2]
QItemEditorFactory * factory
view create()
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
void operator()(QNetworkInformation *information)