summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/CMakeLists.txt3
-rw-r--r--src/network/kernel/qnetworkinformation.cpp556
-rw-r--r--src/network/kernel/qnetworkinformation.h96
-rw-r--r--src/network/kernel/qnetworkinformation_p.h105
-rw-r--r--tests/auto/network/kernel/CMakeLists.txt1
-rw-r--r--tests/auto/network/kernel/qnetworkinformation/CMakeLists.txt6
-rw-r--r--tests/auto/network/kernel/qnetworkinformation/tst_qnetworkinformation.cpp156
7 files changed, 922 insertions, 1 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 8910f3fece..003e504c92 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -5,7 +5,7 @@
#####################################################################
qt_internal_add_module(Network
- PLUGIN_TYPES networkaccessbackends
+ PLUGIN_TYPES networkaccessbackends networkinformationbackends
SOURCES
access/qabstractnetworkcache.cpp access/qabstractnetworkcache.h access/qabstractnetworkcache_p.h
access/qhsts.cpp access/qhsts_p.h
@@ -30,6 +30,7 @@ qt_internal_add_module(Network
kernel/qhostinfo.cpp kernel/qhostinfo.h kernel/qhostinfo_p.h
kernel/qnetconmonitor_p.h
kernel/qnetworkdatagram.cpp kernel/qnetworkdatagram.h kernel/qnetworkdatagram_p.h
+ kernel/qnetworkinformation.cpp kernel/qnetworkinformation_p.h kernel/qnetworkinformation.h
kernel/qnetworkinterface.cpp kernel/qnetworkinterface.h kernel/qnetworkinterface_p.h
kernel/qnetworkinterface_unix_p.h
kernel/qnetworkproxy.cpp kernel/qnetworkproxy.h
diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp
new file mode 100644
index 0000000000..2b0493ee26
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define DEBUG_LOADING
+
+#include "qnetworkinformation.h"
+#include <QtNetwork/private/qnetworkinformation_p.h>
+#include <QtNetwork/qnetworkinformation.h>
+
+#include <QtCore/private/qobject_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/private/qfactoryloader_p.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcNetInfo)
+Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info");
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ (QNetworkInformationBackendFactory_iid,
+ QStringLiteral("/networkinformationbackends")))
+
+struct QStaticNetworkInformationDataHolder
+{
+ QMutex instanceMutex;
+ std::unique_ptr<QNetworkInformation> instanceHolder;
+ QList<QNetworkInformationBackendFactory *> factories;
+};
+Q_GLOBAL_STATIC(QStaticNetworkInformationDataHolder, dataHolder);
+
+class QNetworkInformationPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QNetworkInformation)
+public:
+ QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) { }
+
+ static QNetworkInformation *create(QNetworkInformation::Features features);
+ static QNetworkInformation *create(QStringView name);
+ static QNetworkInformation *instance()
+ {
+ if (!dataHolder())
+ return nullptr;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ return dataHolder->instanceHolder.get();
+ }
+ static QStringList backendNames();
+ static void addToList(QNetworkInformationBackendFactory *factory);
+ static void removeFromList(QNetworkInformationBackendFactory *factory);
+
+private:
+ static bool initializeList();
+
+ std::unique_ptr<QNetworkInformationBackend> backend;
+ bool m_isOnline = true; // cached
+};
+
+bool QNetworkInformationPrivate::initializeList()
+{
+ if (!loader())
+ return false;
+ if (!dataHolder())
+ return false;
+ static QBasicMutex mutex;
+ QMutexLocker initLocker(&mutex);
+
+#if QT_CONFIG(library)
+ loader->update();
+#endif
+ // Instantiates the plugins (and registers the factories)
+ int index = 0;
+ while (loader->instance(index))
+ ++index;
+ initLocker.unlock();
+
+ // Now sort the list on number of features available (then name)
+ const auto featuresNameOrder = [](QNetworkInformationBackendFactory *a,
+ QNetworkInformationBackendFactory *b) {
+ if (!a || !b)
+ return a && !b;
+ auto aFeaturesSupported = qPopulationCount(unsigned(a->featuresSupported()));
+ auto bFeaturesSupported = qPopulationCount(unsigned(b->featuresSupported()));
+ return aFeaturesSupported > bFeaturesSupported
+ || (aFeaturesSupported == bFeaturesSupported
+ && a->name().compare(b->name(), Qt::CaseInsensitive) < 0);
+ };
+ QMutexLocker instanceLocker(&dataHolder->instanceMutex);
+ std::sort(dataHolder->factories.begin(), dataHolder->factories.end(), featuresNameOrder);
+
+ return !dataHolder->factories.isEmpty();
+}
+
+void QNetworkInformationPrivate::addToList(QNetworkInformationBackendFactory *factory)
+{
+ // @note: factory is in the base class ctor
+ if (!dataHolder())
+ return;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ dataHolder->factories.append(factory);
+}
+
+void QNetworkInformationPrivate::removeFromList(QNetworkInformationBackendFactory *factory)
+{
+ // @note: factory is in the base class dtor
+ if (!dataHolder.exists())
+ return;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ dataHolder->factories.removeAll(factory);
+}
+
+QStringList QNetworkInformationPrivate::backendNames()
+{
+ if (!dataHolder())
+ return {};
+ if (!initializeList())
+ return {};
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ const QList copy = dataHolder->factories;
+ locker.unlock();
+
+ QStringList result;
+ result.reserve(copy.size());
+ for (const auto *factory : copy)
+ result << factory->name();
+ return result;
+}
+
+QNetworkInformation *QNetworkInformationPrivate::create(QStringView name)
+{
+ if (!dataHolder())
+ return nullptr;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+#ifdef DEBUG_LOADING
+ qDebug().nospace() << "create() called with name=\"" << name
+ << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
+#endif
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+ locker.unlock();
+ if (!initializeList()) {
+#ifdef DEBUG_LOADING
+ qDebug("Failed to initialize list, returning.");
+#endif
+ return nullptr;
+ }
+ locker.relock();
+
+ QNetworkInformationBackend *backend = nullptr;
+ if (!name.isEmpty()) {
+ const auto nameMatches = [name](QNetworkInformationBackendFactory *factory) {
+ return factory->name().compare(name, Qt::CaseInsensitive) == 0;
+ };
+ auto it = std::find_if(dataHolder->factories.cbegin(), dataHolder->factories.cend(),
+ nameMatches);
+ if (it == dataHolder->factories.cend()) {
+#ifdef DEBUG_LOADING
+ if (dataHolder->factories.isEmpty()) {
+ qDebug("No plugins available");
+ } else {
+ QString listNames;
+ listNames.reserve(8 * dataHolder->factories.count());
+ for (const auto *factory : qAsConst(dataHolder->factories))
+ listNames += factory->name() + QStringLiteral(", ");
+ listNames.chop(2);
+ qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
+ << listNames << " }";
+ }
+#endif
+ return nullptr;
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Creating instance using loader named " << (*it)->name();
+#endif
+ backend = (*it)->create({});
+ } else {
+#ifdef DEBUG_LOADING
+ qDebug() << "Creating instance using loader named" << dataHolder->factories.front()->name();
+#endif
+ if (!dataHolder->factories.isEmpty())
+ backend = dataHolder->factories.front()->create({});
+ }
+ if (!backend)
+ return nullptr;
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ Q_ASSERT(name.isEmpty()
+ || dataHolder->instanceHolder->backendName().compare(name, Qt::CaseInsensitive) == 0);
+ return dataHolder->instanceHolder.get();
+}
+
+QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Features features)
+{
+ if (!dataHolder())
+ return nullptr;
+ QMutexLocker locker(&dataHolder->instanceMutex);
+#ifdef DEBUG_LOADING
+ qDebug().nospace() << "create() called with features=\"" << features
+ << "\". instanceHolder initialized? " << !!dataHolder->instanceHolder;
+#endif
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+ if (features == 0)
+ return nullptr;
+
+ locker.unlock();
+ if (!initializeList()) {
+#ifdef DEBUG_LOADING
+ qDebug("Failed to initialize list, returning.");
+#endif
+ return nullptr;
+ }
+ locker.relock();
+
+ const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
+ return factory && (factory->featuresSupported() & features) == features;
+ };
+
+ for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
+ ++it) {
+ it = std::find_if(it, end, supportsRequestedFeatures);
+ if (it == end) {
+#ifdef DEBUG_LOADING
+ if (dataHolder->factories.isEmpty()) {
+ qDebug("No plugins available");
+ } else {
+ QStringList names;
+ names.reserve(dataHolder->factories.count());
+ for (const auto *factory : qAsConst(dataHolder->factories))
+ names += factory->name();
+ qDebug() << "None of the following backends has all the requested features:"
+ << names << features;
+ }
+#endif
+ break;
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Creating instance using loader named" << (*it)->name();
+#endif
+ if (QNetworkInformationBackend *backend = (*it)->create(features)) {
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ Q_ASSERT(dataHolder->instanceHolder->supports(features));
+ return dataHolder->instanceHolder.get();
+ }
+#ifdef DEBUG_LOADING
+ else {
+ qDebug() << "The factory returned a nullptr";
+ }
+#endif
+ }
+#ifdef DEBUG_LOADING
+ qDebug() << "Couldn't find/create an appropriate backend.";
+#endif
+ return nullptr;
+}
+
+/*!
+ \class QNetworkInformationBackend
+ \internal (Semi-private)
+ \brief QNetworkInformationBackend provides the interface with
+ which QNetworkInformation does all of its actual work.
+
+ Deriving from and implementing this class makes it a candidate
+ for use with QNetworkInformation. The derived class must, on
+ updates, call setters in the QNetworkInformationBackend which
+ will update the values and emit signals if the value has changed.
+
+ \sa QNetworkInformationBackendFactory
+*/
+
+/*!
+ \internal
+ Destroys base backend class.
+*/
+QNetworkInformationBackend::~QNetworkInformationBackend() = default;
+
+/*!
+ \fn QNetworkInformationBackend::name()
+
+ Backend name, return the same in
+ QNetworkInformationBackendFactory::name().
+*/
+
+/*!
+ \fn QNetworkInformation::Features QNetworkInformationBackend::featuresSupported()
+
+ Features supported, return the same in
+ QNetworkInformationBackendFactory::featuresSupported().
+*/
+
+/*!
+ \fn void QNetworkInformationBackend::reachabilityChanged()
+
+ You should not emit this signal manually, call setReachability()
+ instead which will emit this signal when the value changes.
+
+ \sa setReachability
+*/
+
+/*!
+ \fn void QNetworkInformationBackend::setReachability(QNetworkInformation::Reachability reachability)
+
+ Call this when reachability has changed. It will automatically
+ emit reachabilityChanged().
+
+ \sa setReachability
+*/
+
+/*!
+ \class QNetworkInformationBackendFactory
+ \internal (Semi-private)
+ \brief QNetworkInformationBackendFactory provides the interface
+ for creating instances of QNetworkInformationBackend.
+
+ Deriving from and implementing this class will let you register
+ your plugin with QNetworkInformation. It must provide some basic
+ information for querying information about the backend, and must
+ also create the backend if requested. If some pre-conditions for
+ the backend is not met it must return \nullptr.
+*/
+
+/*!
+ \internal
+ Adds the factory to an internal list.
+*/
+QNetworkInformationBackendFactory::QNetworkInformationBackendFactory()
+{
+ QNetworkInformationPrivate::addToList(this);
+}
+
+/*!
+ \internal
+ Removes the factory from an internal list.
+*/
+QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory()
+{
+ QNetworkInformationPrivate::removeFromList(this);
+}
+
+/*!
+ \fn QString QNetworkInformationBackendFactory::name()
+
+ Backend name, return the same in
+ QNetworkInformationBackend::name().
+*/
+
+/*!
+ \fn QNetworkInformation::Features QNetworkInformationBackendFactory::featuresSupported()
+
+ Features supported, return the same in
+ QNetworkInformationBackend::featuresSupported().
+ The factory should not promise support for features that wouldn't
+ be available after creating the backend.
+*/
+
+/*!
+ \fn QNetworkInformationBackend *QNetworkInformationBackendFactory::create()
+
+ Create and return an instance of QNetworkInformationBackend. It
+ will be deallocated by QNetworkInformation on shutdown. If some
+ precondition is not met, meaning the backend would not function
+ correctly, then you must return \nullptr.
+*/
+
+/*!
+ \class QNetworkInformation
+ \since 6.1
+ \brief QNetworkInformation exposes various network information
+ through native backends.
+
+ QNetworkInformation provides a cross-platform interface to
+ network-related information through plugins.
+
+ Various plugins can have various functionality supported, and so
+ you can load() plugins based on which features are needed.
+
+ QNetworkInformation is a singleton and stays alive from the first
+ successful load() until application shutdown.
+
+ \sa QNetworkInformation::Feature
+*/
+
+/*!
+ \enum QNetworkInformation::Feature
+
+ Lists all of the features that a plugin may currently support.
+ This can be used in QNetworkInformation::load().
+
+ \value Reachability
+ If the plugin supports this feature then
+ the \c reachability property will be available.
+ See also QNetworkInformation::Reachability.
+*/
+
+/*!
+ \enum QNetworkInformation::Reachability
+
+ \value Unknown
+ If this value is returned then we may be connected but the OS
+ has still not confirmed full connectivity, or this features
+ is not supported.
+ \value Disconnected
+ Indicates that the system may have no connectivity at all.
+ \value Local
+ Indicates that the system is connected to a network, but it
+ might only be able to access devices on the local network.
+ \value Site
+ Indicates that the system is connected to a network, but it
+ might only be able to access devices on the local subnet or an
+ intranet.
+ \value Online
+ Indicates that the system is connected to a network and
+ able to access the Internet.
+
+ \sa QNetworkInformation::reachability
+*/
+
+/*!
+ \internal ctor
+*/
+QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
+ : QObject(*(new QNetworkInformationPrivate(backend)))
+{
+ connect(backend, &QNetworkInformationBackend::reachabilityChanged, this,
+ [this]() { emit reachabilityChanged(d_func()->backend->m_reachability); });
+}
+
+/*!
+ \internal dtor
+*/
+QNetworkInformation::~QNetworkInformation() = default;
+
+/*!
+ \property QNetworkInformation::reachability
+ \brief The current state of the system's network connectivity.
+
+ Indicates the level of connectivity that can be expected. Do note
+ that this is only based on what the plugin/operating system
+ reports. In certain scenarios this is known to be wrong. For
+ example, on Windows the 'Online' check, by default, is performed
+ by Windows connecting to a Microsoft-owned server. If this server
+ is for any reason blocked then it will assume it does not have
+ Online reachability. Because of this you should not use this as a
+ pre-check before attempting to make a connection.
+*/
+
+QNetworkInformation::Reachability QNetworkInformation::reachability() const
+{
+ return d_func()->backend->m_reachability;
+}
+
+/*!
+ Returns the name of the currently loaded backend.
+*/
+QString QNetworkInformation::backendName() const
+{
+ return d_func()->backend->name();
+}
+
+/*!
+ Returns \c true if the currently loaded backend supports
+ \a features.
+*/
+bool QNetworkInformation::supports(Features features) const
+{
+ return (d_func()->backend->featuresSupported() & features) == features;
+}
+
+/*!
+ Attempts to load a backend whose name matches \a backend
+ (case insensitively).
+
+ Returns \c true if it managed to load the requested backend or
+ if it was already loaded. Returns \c false otherwise
+
+ \sa instance
+*/
+bool QNetworkInformation::load(QStringView backend)
+{
+ auto loadedBackend = QNetworkInformationPrivate::create(backend);
+ return loadedBackend && loadedBackend->backendName() == backend;
+}
+
+/*!
+ Load a backend which supports \a features.
+
+ Returns \c true if it managed to load the requested backend or
+ if it was already loaded. Returns \c false otherwise
+
+ \sa instance
+*/
+bool QNetworkInformation::load(Features features)
+{
+ auto loadedBackend = QNetworkInformationPrivate::create(features);
+ return loadedBackend && loadedBackend->supports(features);
+}
+
+/*!
+ Returns a list of the names of all currently available backends.
+*/
+QStringList QNetworkInformation::availableBackends()
+{
+ return QNetworkInformationPrivate::backendNames();
+}
+
+/*!
+ Returns a pointer to the instance of the QNetworkInformation,
+ if any.
+
+ \sa load()
+*/
+QNetworkInformation *QNetworkInformation::instance()
+{
+ return QNetworkInformationPrivate::instance();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkinformation.h b/src/network/kernel/qnetworkinformation.h
new file mode 100644
index 0000000000..6321300f40
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKINFORMATION_H
+#define QNETWORKINFORMATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qstringview.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkInformationBackend;
+class QNetworkInformationPrivate;
+class Q_NETWORK_EXPORT QNetworkInformation : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QNetworkInformation)
+ Q_PROPERTY(Reachability reachability READ reachability)
+public:
+ enum class Reachability {
+ Unknown,
+ Disconnected,
+ Local,
+ Site,
+ Online,
+ };
+ Q_ENUM(Reachability)
+
+ enum class Feature {
+ Reachability = 0x1,
+ };
+ Q_ENUM(Feature)
+ Q_DECLARE_FLAGS(Features, Feature)
+
+ ~QNetworkInformation();
+
+ Reachability reachability() const;
+
+ QString backendName() const;
+
+ virtual bool supports(Features features) const;
+
+ static bool load(QStringView backend = {});
+ static bool load(Features features);
+ static QStringList availableBackends();
+ static QNetworkInformation *instance();
+
+Q_SIGNALS:
+ void reachabilityChanged(Reachability newReachability);
+
+private:
+ friend class QNetworkInformationPrivate;
+ QNetworkInformation(QNetworkInformationBackend *backend);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/kernel/qnetworkinformation_p.h b/src/network/kernel/qnetworkinformation_p.h
new file mode 100644
index 0000000000..fc783fa079
--- /dev/null
+++ b/src/network/kernel/qnetworkinformation_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKINFORMATION_P_H
+#define QNETWORKINFORMATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Information API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qnetworkinformation.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_NETWORK_EXPORT QNetworkInformationBackend : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~QNetworkInformationBackend();
+
+ virtual QString name() const = 0;
+ virtual QNetworkInformation::Features featuresSupported() const = 0;
+
+Q_SIGNALS:
+ void reachabilityChanged();
+
+protected:
+ void setReachability(QNetworkInformation::Reachability reachability)
+ {
+ if (m_reachability != reachability) {
+ m_reachability = reachability;
+ emit reachabilityChanged();
+ }
+ }
+
+private:
+ QNetworkInformation::Reachability m_reachability = QNetworkInformation::Reachability::Unknown;
+
+ friend class QNetworkInformation;
+ friend class QNetworkInformationPrivate;
+};
+
+class Q_NETWORK_EXPORT QNetworkInformationBackendFactory : public QObject
+{
+ Q_OBJECT
+public:
+ QNetworkInformationBackendFactory();
+ virtual ~QNetworkInformationBackendFactory();
+ virtual QString name() const = 0;
+ virtual QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const = 0;
+ virtual QNetworkInformation::Features featuresSupported() const = 0;
+};
+#define QNetworkInformationBackendFactory_iid "org.qt-project.Qt.NetworkInformationBackendFactory"
+Q_DECLARE_INTERFACE(QNetworkInformationBackendFactory, QNetworkInformationBackendFactory_iid);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/network/kernel/CMakeLists.txt b/tests/auto/network/kernel/CMakeLists.txt
index 8b8960d0ae..89526fa14c 100644
--- a/tests/auto/network/kernel/CMakeLists.txt
+++ b/tests/auto/network/kernel/CMakeLists.txt
@@ -13,4 +13,5 @@ if(QT_FEATURE_private_tests AND NOT MACOS)
endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qauthenticator)
+ add_subdirectory(qnetworkinformation)
endif()
diff --git a/tests/auto/network/kernel/qnetworkinformation/CMakeLists.txt b/tests/auto/network/kernel/qnetworkinformation/CMakeLists.txt
new file mode 100644
index 0000000000..f0c37913a8
--- /dev/null
+++ b/tests/auto/network/kernel/qnetworkinformation/CMakeLists.txt
@@ -0,0 +1,6 @@
+qt_internal_add_test(tst_qnetworkinformation
+ SOURCES
+ tst_qnetworkinformation.cpp
+ PUBLIC_LIBRARIES
+ Qt::NetworkPrivate
+)
diff --git a/tests/auto/network/kernel/qnetworkinformation/tst_qnetworkinformation.cpp b/tests/auto/network/kernel/qnetworkinformation/tst_qnetworkinformation.cpp
new file mode 100644
index 0000000000..09fa65273d
--- /dev/null
+++ b/tests/auto/network/kernel/qnetworkinformation/tst_qnetworkinformation.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtNetwork/private/qnetworkinformation_p.h>
+#include <QtNetwork/qnetworkinformation.h>
+#include <QtTest/qtest.h>
+
+#include <limits>
+#include <memory>
+
+class MockFactory;
+class tst_QNetworkInformation : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void reachability();
+ void cleanupTestCase();
+
+private:
+ std::unique_ptr<MockFactory> mockFactory;
+};
+
+static const QString mockName = QStringLiteral("mock");
+class MockBackend : public QNetworkInformationBackend
+{
+ Q_OBJECT
+public:
+ MockBackend()
+ {
+ Q_ASSERT(!instance);
+ instance = this;
+ setReachability(QNetworkInformation::Reachability::Online);
+ }
+ ~MockBackend() { instance = nullptr; }
+
+ QString name() const override { return mockName; }
+
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return featuresSupportedStatic();
+ }
+
+ static void setNewReachability(QNetworkInformation::Reachability value)
+ {
+ Q_ASSERT(instance);
+ instance->setReachability(value);
+ }
+
+ static QNetworkInformation::Features featuresSupportedStatic()
+ {
+ return { QNetworkInformation::Feature::Reachability };
+ }
+
+private:
+ static inline MockBackend *instance = nullptr;
+};
+
+class MockFactory : public QNetworkInformationBackendFactory
+{
+ Q_OBJECT
+public:
+ QString name() const override { return mockName; }
+ QNetworkInformationBackend *
+ create(QNetworkInformation::Features requiredFeatures) const override
+ {
+ if ((requiredFeatures & featuresSupported()) != requiredFeatures)
+ return nullptr;
+ return new MockBackend();
+ }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return MockBackend::featuresSupportedStatic();
+ }
+};
+
+void tst_QNetworkInformation::initTestCase()
+{
+ auto prevBackends = QNetworkInformation::availableBackends();
+ qDebug() << "available backends:" << prevBackends;
+ // Creating the factory registers it as a backend
+ mockFactory = std::make_unique<MockFactory>();
+ auto backends = QNetworkInformation::availableBackends();
+ QVERIFY(backends.size() > prevBackends.size());
+ QVERIFY(backends.contains(u"mock"));
+ QVERIFY(QNetworkInformation::load(u"mock"));
+ QVERIFY(QNetworkInformation::load(u"mock"));
+ QVERIFY(!QNetworkInformation::load(u"mocks"));
+}
+
+void tst_QNetworkInformation::cleanupTestCase()
+{
+ // Make sure the factory gets unregistered on destruction:
+ mockFactory.reset();
+ auto backends = QNetworkInformation::availableBackends();
+ QVERIFY(!backends.contains(u"mock"));
+}
+
+void tst_QNetworkInformation::reachability()
+{
+ auto info = QNetworkInformation::instance();
+ QNetworkInformation::Reachability boundIsOnline = QNetworkInformation::Reachability::Unknown;
+ bool signalEmitted = false;
+
+ connect(info, &QNetworkInformation::reachabilityChanged, this, [&, info]() {
+ signalEmitted = true;
+ boundIsOnline = info->reachability();
+ });
+ QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Online);
+ MockBackend::setNewReachability(QNetworkInformation::Reachability::Disconnected);
+ QCoreApplication::processEvents();
+ QVERIFY(signalEmitted);
+ QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Disconnected);
+ QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Disconnected);
+
+ // Set the same value again, signal should not be emitted again
+ signalEmitted = false;
+ MockBackend::setNewReachability(QNetworkInformation::Reachability::Disconnected);
+ QCoreApplication::processEvents();
+ QVERIFY(!signalEmitted);
+
+ MockBackend::setNewReachability(QNetworkInformation::Reachability::Local);
+ QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Local);
+ QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Local);
+ MockBackend::setNewReachability(QNetworkInformation::Reachability::Site);
+ QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Site);
+ QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Site);
+}
+
+QTEST_MAIN(tst_QNetworkInformation);
+#include "tst_qnetworkinformation.moc"