diff options
Diffstat (limited to 'src/network/kernel/qnetworkinformation.cpp')
-rw-r--r-- | src/network/kernel/qnetworkinformation.cpp | 396 |
1 files changed, 309 insertions, 87 deletions
diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp index d10024bf05..10d6b89e2c 100644 --- a/src/network/kernel/qnetworkinformation.cpp +++ b/src/network/kernel/qnetworkinformation.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // #define DEBUG_LOADING @@ -46,6 +10,7 @@ #include <QtCore/private/qobject_p.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qmutex.h> +#include <QtCore/qthread.h> #include <QtCore/private/qfactoryloader_p.h> #include <algorithm> @@ -56,26 +21,58 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcNetInfo) Q_LOGGING_CATEGORY(lcNetInfo, "qt.network.info"); -Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, +struct QNetworkInformationDeleter +{ + void operator()(QNetworkInformation *information) { delete information; } +}; + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader, (QNetworkInformationBackendFactory_iid, - QStringLiteral("/networkinformationbackends"))) + QStringLiteral("/networkinformation"))) struct QStaticNetworkInformationDataHolder { QMutex instanceMutex; - std::unique_ptr<QNetworkInformation> instanceHolder; + std::unique_ptr<QNetworkInformation, QNetworkInformationDeleter> instanceHolder; QList<QNetworkInformationBackendFactory *> factories; }; Q_GLOBAL_STATIC(QStaticNetworkInformationDataHolder, dataHolder); +static void networkInfoCleanup() +{ + if (!dataHolder.exists()) + return; + QMutexLocker locker(&dataHolder->instanceMutex); + QNetworkInformation *instance = dataHolder->instanceHolder.get(); + if (!instance) + return; + + dataHolder->instanceHolder.reset(); +} + +using namespace Qt::Literals::StringLiterals; + +class QNetworkInformationDummyBackend : public QNetworkInformationBackend { + Q_OBJECT +public: + QString name() const override { return u"dummy"_s; } + QNetworkInformation::Features featuresSupported() const override + { + return {}; + } +}; + class QNetworkInformationPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QNetworkInformation) public: - QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) { } + QNetworkInformationPrivate(QNetworkInformationBackend *backend) : backend(backend) { + qAddPostRoutine(&networkInfoCleanup); + } static QNetworkInformation *create(QNetworkInformation::Features features); static QNetworkInformation *create(QStringView name); + static QNetworkInformation *createDummy(); static QNetworkInformation *instance() { if (!dataHolder()) @@ -95,19 +92,19 @@ private: bool QNetworkInformationPrivate::initializeList() { - if (!loader()) + if (!qniLoader()) return false; if (!dataHolder()) return false; - static QBasicMutex mutex; + Q_CONSTINIT static QBasicMutex mutex; QMutexLocker initLocker(&mutex); #if QT_CONFIG(library) - loader->update(); + qniLoader->update(); #endif // Instantiates the plugins (and registers the factories) int index = 0; - while (loader->instance(index)) + while (qniLoader->instance(index)) ++index; initLocker.unlock(); @@ -166,6 +163,8 @@ QStringList QNetworkInformationPrivate::backendNames() QNetworkInformation *QNetworkInformationPrivate::create(QStringView name) { + if (name.isEmpty()) + return nullptr; if (!dataHolder()) return nullptr; #ifdef DEBUG_LOADING @@ -184,40 +183,31 @@ QNetworkInformation *QNetworkInformationPrivate::create(QStringView name) return dataHolder->instanceHolder.get(); - 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()) { + 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; + if (dataHolder->factories.isEmpty()) { + qDebug("No plugins available"); + } else { + QString listNames; + listNames.reserve(8 * dataHolder->factories.count()); + for (const auto *factory : std::as_const(dataHolder->factories)) + listNames += factory->name() + ", "_L1; + listNames.chop(2); + qDebug().nospace() << "Couldn't find " << name << " in list with names: { " + << listNames << " }"; } -#ifdef DEBUG_LOADING - qDebug() << "Creating instance using loader named " << (*it)->name(); #endif - backend = (*it)->create({}); - } else { + return nullptr; + } #ifdef DEBUG_LOADING - qDebug() << "Creating instance using loader named" << dataHolder->factories.front()->name(); + qDebug() << "Creating instance using loader named " << (*it)->name(); #endif - if (!dataHolder->factories.isEmpty()) - backend = dataHolder->factories.front()->create({}); - } + QNetworkInformationBackend *backend = (*it)->create((*it)->featuresSupported()); if (!backend) return nullptr; dataHolder->instanceHolder.reset(new QNetworkInformation(backend)); @@ -248,7 +238,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea return dataHolder->instanceHolder.get(); const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) { - return factory && (factory->featuresSupported() & features) == features; + return factory && factory->featuresSupported().testFlags(features); }; for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end; @@ -261,7 +251,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea } else { QStringList names; names.reserve(dataHolder->factories.count()); - for (const auto *factory : qAsConst(dataHolder->factories)) + for (const auto *factory : std::as_const(dataHolder->factories)) names += factory->name(); qDebug() << "None of the following backends has all the requested features:" << names << features; @@ -289,6 +279,20 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea return nullptr; } +QNetworkInformation *QNetworkInformationPrivate::createDummy() +{ + if (!dataHolder()) + return nullptr; + + QMutexLocker locker(&dataHolder->instanceMutex); + if (dataHolder->instanceHolder) + return dataHolder->instanceHolder.get(); + + QNetworkInformationBackend *backend = new QNetworkInformationDummyBackend; + dataHolder->instanceHolder.reset(new QNetworkInformation(backend)); + return dataHolder->instanceHolder.get(); +} + /*! \class QNetworkInformationBackend \internal (Semi-private) @@ -399,6 +403,7 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() /*! \class QNetworkInformation + \inmodule QtNetwork \since 6.1 \brief QNetworkInformation exposes various network information through native backends. @@ -410,7 +415,9 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() 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. + successful load() until destruction of the QCoreApplication object. + If you destroy and re-create the QCoreApplication object you must call + load() again. \sa QNetworkInformation::Feature */ @@ -422,9 +429,26 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() This can be used in QNetworkInformation::load(). \value Reachability - If the plugin supports this feature then - the \c reachability property will be available. + If the plugin supports this feature then the \c reachability property + will provide useful results. Otherwise it will always return + \c{Reachability::Unknown}. See also QNetworkInformation::Reachability. + + \value CaptivePortal + If the plugin supports this feature then the \c isBehindCaptivePortal + property will provide useful results. Otherwise it will always return + \c{false}. + + \value TransportMedium + If the plugin supports this feature then the \c transportMedium + property will provide useful results. Otherwise it will always return + \c{TransportMedium::Unknown}. + See also QNetworkInformation::TransportMedium. + + \value Metered + If the plugin supports this feature then the \c isMetered + property will provide useful results. Otherwise it will always return + \c{false}. */ /*! @@ -432,7 +456,7 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() \value Unknown If this value is returned then we may be connected but the OS - has still not confirmed full connectivity, or this features + has still not confirmed full connectivity, or this feature is not supported. \value Disconnected Indicates that the system may have no connectivity at all. @@ -451,13 +475,53 @@ QNetworkInformationBackendFactory::~QNetworkInformationBackendFactory() */ /*! + \enum QNetworkInformation::TransportMedium + \since 6.3 + + Lists the currently recognized media with which one can connect to the + internet. + + \value Unknown + Returned if either the OS reports no active medium, the active medium is + not recognized by Qt, or the TransportMedium feature is not supported. + \value Ethernet + Indicates that the currently active connection is using ethernet. + Note: This value may also be returned when Windows is connected to a + Bluetooth personal area network. + \value Cellular + Indicates that the currently active connection is using a cellular + network. + \value WiFi + Indicates that the currently active connection is using Wi-Fi. + \value Bluetooth + Indicates that the currently active connection is connected using + Bluetooth. + + \sa QNetworkInformation::transportMedium +*/ + +/*! \internal ctor */ QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend) : QObject(*(new QNetworkInformationPrivate(backend))) { connect(backend, &QNetworkInformationBackend::reachabilityChanged, this, - [this]() { emit reachabilityChanged(d_func()->backend->reachability()); }); + &QNetworkInformation::reachabilityChanged); + connect(backend, &QNetworkInformationBackend::behindCaptivePortalChanged, this, + &QNetworkInformation::isBehindCaptivePortalChanged); + connect(backend, &QNetworkInformationBackend::transportMediumChanged, this, + &QNetworkInformation::transportMediumChanged); + connect(backend, &QNetworkInformationBackend::isMeteredChanged, this, + &QNetworkInformation::isMeteredChanged); + + QThread *main = nullptr; + + if (QCoreApplication::instance()) + main = QCoreApplication::instance()->thread(); + + if (main && thread() != main) + moveToThread(main); } /*! @@ -485,6 +549,55 @@ QNetworkInformation::Reachability QNetworkInformation::reachability() const } /*! + \property QNetworkInformation::isBehindCaptivePortal + \brief Lets you know if the user's device is behind a captive portal. + \since 6.2 + + This property indicates if the user's device is currently known to be + behind a captive portal. This functionality relies on the operating system's + detection of captive portals and is not supported on systems that don't + report this. On systems where this is not supported this will always return + \c{false}. +*/ +bool QNetworkInformation::isBehindCaptivePortal() const +{ + return d_func()->backend->behindCaptivePortal(); +} + +/*! + \property QNetworkInformation::transportMedium + \brief The currently active transport medium for the application + \since 6.3 + + This property returns the currently active transport medium for the + application, on operating systems where such information is available. + + When the current transport medium changes a signal is emitted, this can, + for instance, occur when a user leaves the range of a WiFi network, unplugs + their ethernet cable or enables Airplane mode. +*/ +QNetworkInformation::TransportMedium QNetworkInformation::transportMedium() const +{ + return d_func()->backend->transportMedium(); +} + +/*! + \property QNetworkInformation::isMetered + \brief Check if the current connection is metered + \since 6.3 + + This property returns whether the current connection is (known to be) + metered or not. You can use this as a guiding factor to decide whether your + application should perform certain network requests or uploads. + For instance, you may not want to upload logs or diagnostics while this + property is \c true. +*/ +bool QNetworkInformation::isMetered() const +{ + return d_func()->backend->isMetered(); +} + +/*! Returns the name of the currently loaded backend. */ QString QNetworkInformation::backendName() const @@ -502,34 +615,139 @@ bool QNetworkInformation::supports(Features features) const } /*! + \since 6.3 + + Returns all the supported features of the current backend. +*/ +QNetworkInformation::Features QNetworkInformation::supportedFeatures() const +{ + return d_func()->backend->featuresSupported(); +} + +/*! + \since 6.3 + + Attempts to load the platform-default backend. + + \note Starting with 6.7 this tries to load any backend that supports + \l{QNetworkInformation::Feature::Reachability}{Reachability} if the + platform-default backend is not available or fails to load. + If this also fails it will fall back to a backend that only returns + the default values for all properties. + + This platform-to-plugin mapping is as follows: + + \table + \header + \li Platform + \li Plugin-name + \row + \li Windows + \li networklistmanager + \row + \li Apple (macOS/iOS) + \li scnetworkreachability + \row + \li Android + \li android + \row + \li Linux + \li networkmanager + \endtable + + This function is provided for convenience where the logic earlier + is good enough. If you require a specific plugin then you should call + loadBackendByName() or loadBackendByFeatures() directly instead. + + Determines a suitable backend to load and returns \c true if this backend + is already loaded or on successful loading of it. Returns \c false if any + other backend has already been loaded, or if loading of the selected + backend fails. + + \sa instance(), load() +*/ +bool QNetworkInformation::loadDefaultBackend() +{ + int index = -1; +#ifdef Q_OS_WIN + index = QNetworkInformationBackend::PluginNamesWindowsIndex; +#elif defined(Q_OS_DARWIN) + index = QNetworkInformationBackend::PluginNamesAppleIndex; +#elif defined(Q_OS_ANDROID) + index = QNetworkInformationBackend::PluginNamesAndroidIndex; +#elif defined(Q_OS_LINUX) + index = QNetworkInformationBackend::PluginNamesLinuxIndex; +#endif + if (index != -1 && loadBackendByName(QNetworkInformationBackend::PluginNames[index])) + return true; + // We assume reachability is the most commonly wanted feature, and try to + // load the backend that advertises the most features including that: + if (loadBackendByFeatures(Feature::Reachability)) + return true; + + // Fall back to the dummy backend + return loadBackendByName(u"dummy"); +} + +/*! + \since 6.4 + 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 + if it was already loaded. Returns \c false otherwise. \sa instance */ -bool QNetworkInformation::load(QStringView backend) +bool QNetworkInformation::loadBackendByName(QStringView backend) { + if (backend == u"dummy") + return QNetworkInformationPrivate::createDummy() != nullptr; + auto loadedBackend = QNetworkInformationPrivate::create(backend); - return loadedBackend && loadedBackend->backendName() == backend; + return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0; } +#if QT_DEPRECATED_SINCE(6,4) /*! + \deprecated [6.4] Use loadBackendByName() instead. + + \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures() +*/ +bool QNetworkInformation::load(QStringView backend) +{ + return loadBackendByName(backend); +} +#endif // QT_DEPRECATED_SINCE(6,4) + +/*! + \since 6.4 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 + if it was already loaded. Returns \c false otherwise. \sa instance */ -bool QNetworkInformation::load(Features features) +bool QNetworkInformation::loadBackendByFeatures(Features features) { auto loadedBackend = QNetworkInformationPrivate::create(features); return loadedBackend && loadedBackend->supports(features); } +#if QT_DEPRECATED_SINCE(6,4) +/*! + \deprecated [6.4] Use loadBackendByFeatures() instead. + + \sa loadBackendByName(), loadDefaultBackend(), loadBackendByFeatures() +*/ +bool QNetworkInformation::load(Features features) +{ + return loadBackendByFeatures(features); +} +#endif // QT_DEPRECATED_SINCE(6,4) + /*! Returns a list of the names of all currently available backends. */ @@ -550,3 +768,7 @@ QNetworkInformation *QNetworkInformation::instance() } QT_END_NAMESPACE + +#include "moc_qnetworkinformation.cpp" +#include "moc_qnetworkinformation_p.cpp" +#include "qnetworkinformation.moc" |