diff options
author | Mårten Nordheim <marten.nordheim@qt.io> | 2021-06-03 15:51:07 +0200 |
---|---|---|
committer | Mårten Nordheim <marten.nordheim@qt.io> | 2021-06-11 19:23:03 +0200 |
commit | d245e3a7883ccb2c8beabbca5094de54ec963644 (patch) | |
tree | 731cd57605802d03d3193540db147727ad38a077 /src/plugins/networkinformation | |
parent | f6f6d06028a62fef3e0d8458903549499fced45a (diff) |
QNetworkInformation: Rename the plugins and type
To better match other plugins in Qt
Pick-to: 6.2
Change-Id: I6ab19603f13c80a8afe4fe69f64669559a0eea15
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Yuhang Zhao <2546789017@qq.com>
Diffstat (limited to 'src/plugins/networkinformation')
14 files changed, 1609 insertions, 0 deletions
diff --git a/src/plugins/networkinformation/CMakeLists.txt b/src/plugins/networkinformation/CMakeLists.txt new file mode 100644 index 0000000000..142031c62a --- /dev/null +++ b/src/plugins/networkinformation/CMakeLists.txt @@ -0,0 +1,15 @@ +if(WIN32 AND QT_FEATURE_networklistmanager) + add_subdirectory(networklistmanager) +endif() + +if(LINUX AND TARGET Qt::DBus) + add_subdirectory(networkmanager) +endif() + +if(APPLE) + add_subdirectory(scnetworkreachability) +endif() + +if(ANDROID) + add_subdirectory(android) +endif() diff --git a/src/plugins/networkinformation/android/CMakeLists.txt b/src/plugins/networkinformation/android/CMakeLists.txt new file mode 100644 index 0000000000..f1d260547a --- /dev/null +++ b/src/plugins/networkinformation/android/CMakeLists.txt @@ -0,0 +1,43 @@ + +set(java_sources + jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java +) + +qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend + INCLUDE_JARS ${QT_ANDROID_JAR} + SOURCES ${java_sources} + OUTPUT_DIR "${QT_BUILD_DIR}/jar" +) + +install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend + DESTINATION jar + COMPONENT Devel +) + +qt_internal_add_plugin(QAndroidNetworkInformationPlugin + OUTPUT_NAME androidnetworkinformation + CLASS_NAME QAndroidNetworkInformationBackendFactory + TYPE networkinformation + DEFAULT_IF ANDROID + SOURCES + qandroidnetworkinformationbackend.cpp + wrapper/androidconnectivitymanager.cpp wrapper/androidconnectivitymanager.h + LIBRARIES + Qt::NetworkPrivate + DEFINES + QT_USE_QSTRINGBUILDER +) + +set_property( + TARGET + QAndroidNetworkInformationPlugin + APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES + jar/Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend.jar +) + +set_property( + TARGET + QAndroidNetworkInformationPlugin + APPEND PROPERTY QT_ANDROID_PERMISSIONS + android.permission.ACCESS_NETWORK_STATE +) diff --git a/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java new file mode 100644 index 0000000000..0e89d23ab4 --- /dev/null +++ b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +package org.qtproject.qt.android.networkinformation; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.net.NetworkCapabilities; +import android.net.Network; + +public class QtAndroidNetworkInformation { + private static final String LOG_TAG = "QtAndroidNetworkInformation"; + + private static native void connectivityChanged(); + private static native void behindCaptivePortalChanged(boolean state); + + private static QtNetworkInformationCallback m_callback = null; + private static final Object m_lock = new Object(); + + enum AndroidConnectivity { + Connected, Unknown, Disconnected + } + + private static class QtNetworkInformationCallback extends NetworkCallback { + public AndroidConnectivity previousState = null; + + QtNetworkInformationCallback() { + } + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) { + AndroidConnectivity s; + if (!capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) + s = AndroidConnectivity.Disconnected; + else if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) + s = AndroidConnectivity.Connected; + else + s = AndroidConnectivity.Unknown; // = we _may_ have Internet access + setState(s); + + final boolean captive + = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); + behindCaptivePortalChanged(captive); + } + + private void setState(AndroidConnectivity s) { + if (previousState != s) { + previousState = s; + connectivityChanged(); + } + } + + @Override + public void onLost(Network network) { + setState(AndroidConnectivity.Disconnected); + } + } + + private QtAndroidNetworkInformation() { + } + + public static AndroidConnectivity state() { + if (m_callback != null && m_callback.previousState != null) + return m_callback.previousState; + return AndroidConnectivity.Unknown; + } + + public static void registerReceiver(final Context context) { + synchronized (m_lock) { + if (m_callback == null) { + ConnectivityManager manager = getConnectivityManager(context); + m_callback = new QtNetworkInformationCallback(); + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) + builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND); + NetworkRequest request = builder.build(); + manager.registerNetworkCallback(request, m_callback); + } + } + } + + public static void unregisterReceiver(final Context context) { + synchronized (m_lock) { + if (m_callback != null) { + getConnectivityManager(context).unregisterNetworkCallback(m_callback); + m_callback = null; + } + } + } + + public static ConnectivityManager getConnectivityManager(final Context context) { + return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + } +} diff --git a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp new file mode 100644 index 0000000000..9eef471989 --- /dev/null +++ b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QtNetwork/private/qnetworkinformation_p.h> + +#include "wrapper/androidconnectivitymanager.h" + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoAndroid) +Q_LOGGING_CATEGORY(lcNetInfoAndroid, "qt.network.info.android"); + +static const QString backendName = QStringLiteral("android"); + +class QAndroidNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QAndroidNetworkInformationBackend(); + ~QAndroidNetworkInformationBackend() { m_valid = false; } + + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return featuresSupportedStatic(); + } + + static QNetworkInformation::Features featuresSupportedStatic() + { + using Feature = QNetworkInformation::Feature; + return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal); + } + + bool isValid() { return m_valid; } + +private: + Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend); + bool m_valid = false; +}; + +class QAndroidNetworkInformationBackendFactory : public QNetworkInformationBackendFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid) + Q_INTERFACES(QNetworkInformationBackendFactory) +public: + QAndroidNetworkInformationBackendFactory() = default; + ~QAndroidNetworkInformationBackendFactory() = default; + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return QAndroidNetworkInformationBackend::featuresSupportedStatic(); + } + + QNetworkInformationBackend * + create(QNetworkInformation::Features requiredFeatures) const override + { + if ((requiredFeatures & featuresSupported()) != requiredFeatures) + return nullptr; + auto backend = new QAndroidNetworkInformationBackend(); + if (!backend->isValid()) + delete std::exchange(backend, nullptr); + return backend; + } + +private: + Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackendFactory); +}; + +QAndroidNetworkInformationBackend::QAndroidNetworkInformationBackend() +{ + auto conman = AndroidConnectivityManager::getInstance(); + if (!conman) + return; + m_valid = true; + setReachability(QNetworkInformation::Reachability::Unknown); + connect(conman, &AndroidConnectivityManager::connectivityChanged, this, [this, conman]() { + static const auto mapState = [](AndroidConnectivityManager::AndroidConnectivity state) { + switch (state) { + case AndroidConnectivityManager::AndroidConnectivity::Connected: + return QNetworkInformation::Reachability::Online; + case AndroidConnectivityManager::AndroidConnectivity::Disconnected: + return QNetworkInformation::Reachability::Disconnected; + case AndroidConnectivityManager::AndroidConnectivity::Unknown: + default: + return QNetworkInformation::Reachability::Unknown; + } + }; + + setReachability(mapState(conman->networkConnectivity())); + }); + + connect(conman, &AndroidConnectivityManager::captivePortalChanged, this, + &QAndroidNetworkInformationBackend::setBehindCaptivePortal); +} + +QT_END_NAMESPACE + +#include "qandroidnetworkinformationbackend.moc" diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp new file mode 100644 index 0000000000..e88fe7d955 --- /dev/null +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#include "androidconnectivitymanager.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qjnienvironment.h> + +QT_BEGIN_NAMESPACE + +using namespace QNativeInterface; + +struct AndroidConnectivityManagerInstance +{ + AndroidConnectivityManagerInstance() : connManager(new AndroidConnectivityManager) { } + std::unique_ptr<AndroidConnectivityManager> connManager = nullptr; +}; +Q_GLOBAL_STATIC(AndroidConnectivityManagerInstance, androidConnManagerInstance) + +static const char networkInformationClass[] = + "org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation"; + +static void networkConnectivityChanged(JNIEnv *env, jobject obj) +{ + Q_UNUSED(env); + Q_UNUSED(obj); + Q_EMIT androidConnManagerInstance->connManager->connectivityChanged(); +} + +static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean state) +{ + Q_UNUSED(env); + Q_UNUSED(obj); + Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state); +} + +AndroidConnectivityManager::AndroidConnectivityManager() +{ + if (!registerNatives()) + return; + + m_connectivityManager = QJniObject::callStaticObjectMethod( + networkInformationClass, "getConnectivityManager", + "(Landroid/content/Context;)Landroid/net/ConnectivityManager;", + QAndroidApplication::context()); + if (!m_connectivityManager.isValid()) + return; + + QJniObject::callStaticMethod<void>(networkInformationClass, "registerReceiver", + "(Landroid/content/Context;)V", QAndroidApplication::context()); +} + +AndroidConnectivityManager *AndroidConnectivityManager::getInstance() +{ + if (!androidConnManagerInstance()) + return nullptr; + return androidConnManagerInstance->connManager->isValid() + ? androidConnManagerInstance->connManager.get() + : nullptr; +} + +AndroidConnectivityManager::~AndroidConnectivityManager() +{ + QJniObject::callStaticMethod<void>(networkInformationClass, "unregisterReceiver", + "(Landroid/content/Context;)V", QAndroidApplication::context()); +} + +AndroidConnectivityManager::AndroidConnectivity AndroidConnectivityManager::networkConnectivity() +{ + QJniEnvironment env; + QJniObject networkReceiver(networkInformationClass); + jclass clazz = env->GetObjectClass(networkReceiver.object()); + static const QByteArray functionSignature = + QByteArray(QByteArray("()L") + networkInformationClass + "$AndroidConnectivity;"); + QJniObject enumObject = + QJniObject::callStaticObjectMethod(clazz, "state", functionSignature.data()); + if (!enumObject.isValid()) + return AndroidConnectivityManager::AndroidConnectivity::Unknown; + + QJniObject enumName = enumObject.callObjectMethod<jstring>("name"); + if (!enumName.isValid()) + return AndroidConnectivityManager::AndroidConnectivity::Unknown; + + QString name = enumName.toString(); + if (name == u"Connected") + return AndroidConnectivity::Connected; + if (name == u"Disconnected") + return AndroidConnectivity::Disconnected; + return AndroidConnectivity::Unknown; +} + +bool AndroidConnectivityManager::registerNatives() +{ + QJniEnvironment env; + QJniObject networkReceiver(networkInformationClass); + if (!networkReceiver.isValid()) + return false; + + jclass clazz = env->GetObjectClass(networkReceiver.object()); + static JNINativeMethod methods[] = { + { "connectivityChanged", "()V", reinterpret_cast<void *>(networkConnectivityChanged) }, + { "behindCaptivePortalChanged", "(Z)V", + reinterpret_cast<void *>(behindCaptivePortalChanged) } + }; + const bool ret = (env->RegisterNatives(clazz, methods, std::size(methods)) == JNI_OK); + env->DeleteLocalRef(clazz); + return ret; +} + +QT_END_NAMESPACE diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h new file mode 100644 index 0000000000..14f5aa9b57 --- /dev/null +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 ANDROIDCONNECTIVITYMANAGER_H +#define ANDROIDCONNECTIVITYMANAGER_H + +#include <QObject> +#include <QtCore/qjniobject.h> + +QT_BEGIN_NAMESPACE + +class AndroidConnectivityManager : public QObject +{ + Q_OBJECT +public: + enum class AndroidConnectivity { Connected, Unknown, Disconnected }; + Q_ENUM(AndroidConnectivity); + static AndroidConnectivityManager *getInstance(); + ~AndroidConnectivityManager(); + + AndroidConnectivity networkConnectivity(); + inline bool isValid() const { return m_connectivityManager.isValid(); } + +Q_SIGNALS: + void connectivityChanged(); + void captivePortalChanged(bool state); + +private: + friend struct AndroidConnectivityManagerInstance; + AndroidConnectivityManager(); + bool registerNatives(); + QJniObject m_connectivityManager; + + Q_DISABLE_COPY_MOVE(AndroidConnectivityManager); +}; + +QT_END_NAMESPACE + +#endif // ANDROIDCONNECTIVITYMANAGER_H diff --git a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt new file mode 100644 index 0000000000..52f19aefa9 --- /dev/null +++ b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt @@ -0,0 +1,13 @@ +qt_internal_add_plugin(QNLMNIPlugin + OUTPUT_NAME networklistmanager + CLASS_NAME QNetworkListManagerNetworkInformationBackendFactory + TYPE networkinformation + DEFAULT_IF WINDOWS AND QT_FEATURE_networklistmanager + SOURCES qnetworklistmanagernetworkinformationbackend.cpp + PUBLIC_LIBRARIES + Qt::NetworkPrivate +) + +# Don't repeat the target name in AUTOGEN_BUILD_DIR to work around issues with overlong paths. +set_property(TARGET QNLMNIPlugin PROPERTY + AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/autogen") diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp new file mode 100644 index 0000000000..d37f83832b --- /dev/null +++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp @@ -0,0 +1,419 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QtNetwork/private/qnetworkinformation_p.h> + +#include <QtCore/qglobal.h> +#include <QtCore/private/qobject_p.h> +#include <QtCore/qscopeguard.h> + +#include <objbase.h> +#include <netlistmgr.h> +#include <wrl/client.h> +#include <wrl/wrappers/corewrappers.h> +#include <comdef.h> +#include <iphlpapi.h> +using namespace Microsoft::WRL; + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoNLM) +Q_LOGGING_CATEGORY(lcNetInfoNLM, "qt.network.info.netlistmanager"); + +static const QString backendName = QStringLiteral("networklistmanager"); + +namespace { +QString errorStringFromHResult(HRESULT hr) +{ + _com_error error(hr); + return QString::fromWCharArray(error.ErrorMessage()); +} + +template<typename T> +bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject) +{ + if (riid == __uuidof(T)) { + *ppvObject = static_cast<T *>(from); + from->AddRef(); + return true; + } + return false; +} + +bool testCONNECTIVITY(NLM_CONNECTIVITY connectivity, NLM_CONNECTIVITY flag) +{ + return (connectivity & flag) == flag; +} + +QNetworkInformation::Reachability reachabilityFromNLM_CONNECTIVITY(NLM_CONNECTIVITY connectivity) +{ + if (connectivity == NLM_CONNECTIVITY_DISCONNECTED) + return QNetworkInformation::Reachability::Disconnected; + if (testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV6_INTERNET) + || testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV4_INTERNET)) { + return QNetworkInformation::Reachability::Online; + } + if (testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV6_SUBNET) + || testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV4_SUBNET)) { + return QNetworkInformation::Reachability::Site; + } + if (testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV6_LOCALNETWORK) + || testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV4_LOCALNETWORK)) { + return QNetworkInformation::Reachability::Local; + } + if (testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV6_NOTRAFFIC) + || testCONNECTIVITY(connectivity, NLM_CONNECTIVITY_IPV4_NOTRAFFIC)) { + return QNetworkInformation::Reachability::Unknown; + } + + return QNetworkInformation::Reachability::Unknown; +} +} + +class QNetworkListManagerEvents; +class QNetworkListManagerNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QNetworkListManagerNetworkInformationBackend(); + ~QNetworkListManagerNetworkInformationBackend(); + + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return featuresSupportedStatic(); + } + + static QNetworkInformation::Features featuresSupportedStatic() + { + return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability + | QNetworkInformation::Feature::CaptivePortal); + } + + [[nodiscard]] bool start(); + void stop(); + +private: + friend class QNetworkListManagerEvents; + + bool event(QEvent *event) override; + void setConnectivity(NLM_CONNECTIVITY newConnectivity); + void checkCaptivePortal(); + + ComPtr<QNetworkListManagerEvents> managerEvents; + + NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED; + + bool monitoring = false; + bool comInitFailed = false; +}; + +class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid) + Q_INTERFACES(QNetworkInformationBackendFactory) +public: + QNetworkListManagerNetworkInformationBackendFactory() = default; + ~QNetworkListManagerNetworkInformationBackendFactory() = default; + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return QNetworkListManagerNetworkInformationBackend::featuresSupportedStatic(); + } + + QNetworkInformationBackend * + create(QNetworkInformation::Features requiredFeatures) const override + { + if ((requiredFeatures & featuresSupported()) != requiredFeatures) + return nullptr; + auto backend = new QNetworkListManagerNetworkInformationBackend(); + if (!backend->start()) { + qCWarning(lcNetInfoNLM) << "Failed to start listening to events"; + delete backend; + backend = nullptr; + } + return backend; + } +}; + +class QNetworkListManagerEvents : public QObject, public INetworkListManagerEvents +{ + Q_OBJECT +public: + QNetworkListManagerEvents(); + virtual ~QNetworkListManagerEvents(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; + + ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; } + ULONG STDMETHODCALLTYPE Release() override + { + if (--ref == 0) { + delete this; + return 0; + } + return ref; + } + + HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override; + + [[nodiscard]] bool start(); + bool stop(); + + [[nodiscard]] bool checkBehindCaptivePortal(); + +signals: + void connectivityChanged(NLM_CONNECTIVITY); + +private: + ComPtr<INetworkListManager> networkListManager = nullptr; + ComPtr<IConnectionPoint> connectionPoint = nullptr; + + QAtomicInteger<ULONG> ref = 0; + DWORD cookie = 0; +}; + +QNetworkListManagerEvents::QNetworkListManagerEvents() : QObject(nullptr) +{ + auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER, + IID_INetworkListManager, &networkListManager); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Could not get a NetworkListManager instance:" + << errorStringFromHResult(hr); + return; + } + + ComPtr<IConnectionPointContainer> connectionPointContainer; + hr = networkListManager.As(&connectionPointContainer); + if (SUCCEEDED(hr)) { + hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents, + &connectionPoint); + } + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to get connection point for network list manager events:" + << errorStringFromHResult(hr); + } +} + +QNetworkListManagerEvents::~QNetworkListManagerEvents() +{ + Q_ASSERT(ref == 0); +} + +HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject) + || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject) + ? S_OK + : E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE +QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) +{ + // This function is run on a different thread than 'monitor' is created on, so we need to run + // it on that thread + connectivityChanged(newConnectivity); + return S_OK; +} + +bool QNetworkListManagerEvents::start() +{ + if (!connectionPoint) { + qCWarning(lcNetInfoNLM, "Initialization failed, can't start!"); + return false; + } + auto hr = connectionPoint->Advise(this, &cookie); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to subscribe to network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + + // Update connectivity since it might have changed since this class was constructed + NLM_CONNECTIVITY connectivity; + hr = networkListManager->GetConnectivity(&connectivity); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Could not get connectivity:" << errorStringFromHResult(hr); + } else { + emit connectivityChanged(connectivity); + } + return true; +} + +bool QNetworkListManagerEvents::stop() +{ + Q_ASSERT(connectionPoint); + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to unsubscribe from network connectivity events:" + << errorStringFromHResult(hr); + return false; + } + cookie = 0; + return true; +} + +bool QNetworkListManagerEvents::checkBehindCaptivePortal() +{ + if (!networkListManager) + return false; + ComPtr<IEnumNetworks> networks; + HRESULT hr = + networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, networks.GetAddressOf()); + if (FAILED(hr) || networks == nullptr) + return false; + + // @note: This checks all connected networks, but that might not be necessary + ComPtr<INetwork> network; + hr = networks->Next(1, network.GetAddressOf(), nullptr); + while (SUCCEEDED(hr) && network != nullptr) { + ComPtr<IPropertyBag> propertyBag; + hr = network.As(&propertyBag); + if (SUCCEEDED(hr) && propertyBag != nullptr) { + VARIANT variant; + VariantInit(&variant); + const auto scopedVariantClear = qScopeGuard([&variant]() { VariantClear(&variant); }); + + const wchar_t *versions[] = { NA_InternetConnectivityV6, NA_InternetConnectivityV4 }; + for (const auto version : versions) { + hr = propertyBag->Read(version, &variant, nullptr); + if (SUCCEEDED(hr) + && (V_UINT(&variant) & NLM_INTERNET_CONNECTIVITY_WEBHIJACK) + == NLM_INTERNET_CONNECTIVITY_WEBHIJACK) { + return true; + } + } + } + + hr = networks->Next(1, network.GetAddressOf(), nullptr); + } + + return false; +} + +QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformationBackend() +{ + auto hr = CoInitialize(nullptr); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return; + } + managerEvents = new QNetworkListManagerEvents(); + connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this, + &QNetworkListManagerNetworkInformationBackend::setConnectivity); +} + +QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend() +{ + if (comInitFailed) + return; + stop(); +} + +void QNetworkListManagerNetworkInformationBackend::setConnectivity(NLM_CONNECTIVITY newConnectivity) +{ + if (reachabilityFromNLM_CONNECTIVITY(connectivity) + != reachabilityFromNLM_CONNECTIVITY(newConnectivity)) { + connectivity = newConnectivity; + setReachability(reachabilityFromNLM_CONNECTIVITY(newConnectivity)); + + // @future: only check if signal is connected + checkCaptivePortal(); + } +} + +void QNetworkListManagerNetworkInformationBackend::checkCaptivePortal() +{ + setBehindCaptivePortal(managerEvents->checkBehindCaptivePortal()); +} + +bool QNetworkListManagerNetworkInformationBackend::event(QEvent *event) +{ + if (event->type() == QEvent::ThreadChange && monitoring) { + stop(); + QMetaObject::invokeMethod(this, &QNetworkListManagerNetworkInformationBackend::start, + Qt::QueuedConnection); + } + + return QObject::event(event); +} + +bool QNetworkListManagerNetworkInformationBackend::start() +{ + Q_ASSERT(!monitoring); + + if (comInitFailed) { + auto hr = CoInitialize(nullptr); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to initialize COM:" << errorStringFromHResult(hr); + comInitFailed = true; + return false; + } + comInitFailed = false; + } + if (!managerEvents) + managerEvents = new QNetworkListManagerEvents(); + + if (managerEvents->start()) + monitoring = true; + return monitoring; +} + +void QNetworkListManagerNetworkInformationBackend::stop() +{ + if (monitoring) { + Q_ASSERT(managerEvents); + // Can return false but realistically shouldn't since that would break everything: + managerEvents->stop(); + monitoring = false; + managerEvents.Reset(); + } + + CoUninitialize(); + comInitFailed = true; // we check this value in start() to see if we need to re-initialize +} + +QT_END_NAMESPACE + +#include "qnetworklistmanagernetworkinformationbackend.moc" diff --git a/src/plugins/networkinformation/networkmanager/CMakeLists.txt b/src/plugins/networkinformation/networkmanager/CMakeLists.txt new file mode 100644 index 0000000000..900364c32f --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/CMakeLists.txt @@ -0,0 +1,13 @@ +qt_internal_add_plugin(QNetworkManagerNetworkInformationPlugin + OUTPUT_NAME networkmanager + CLASS_NAME QNetworkManagerNetworkInformationBackendFactory + TYPE networkinformation + DEFAULT_IF LINUX + SOURCES + qnetworkmanagernetworkinformationbackend.cpp + qnetworkmanagerservice.h + qnetworkmanagerservice.cpp + PUBLIC_LIBRARIES + Qt::DBus + Qt::NetworkPrivate +) diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp new file mode 100644 index 0000000000..bfb04ae4a6 --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QtNetwork/private/qnetworkinformation_p.h> + +#include "qnetworkmanagerservice.h" + +#include <QtCore/qglobal.h> +#include <QtCore/private/qobject_p.h> + +#include <QtDBus/qdbusmessage.h> + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoNLM) +Q_LOGGING_CATEGORY(lcNetInfoNM, "qt.network.info.networkmanager"); + +namespace { +QNetworkInformation::Reachability reachabilityFromNMState(QNetworkManagerInterface::NMState state) +{ + switch (state) { + case QNetworkManagerInterface::NM_STATE_UNKNOWN: + case QNetworkManagerInterface::NM_STATE_ASLEEP: + case QNetworkManagerInterface::NM_STATE_CONNECTING: + return QNetworkInformation::Reachability::Unknown; + case QNetworkManagerInterface::NM_STATE_DISCONNECTING: // No point in starting new connections: + case QNetworkManagerInterface::NM_STATE_DISCONNECTED: + return QNetworkInformation::Reachability::Disconnected; + case QNetworkManagerInterface::NM_STATE_CONNECTED_LOCAL: + return QNetworkInformation::Reachability::Local; + case QNetworkManagerInterface::NM_STATE_CONNECTED_SITE: + return QNetworkInformation::Reachability::Site; + case QNetworkManagerInterface::NM_STATE_CONNECTED_GLOBAL: + return QNetworkInformation::Reachability::Online; + } + return QNetworkInformation::Reachability::Unknown; +} +} + +static QString backendName = QStringLiteral("networkmanager"); + +class QNetworkManagerNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QNetworkManagerNetworkInformationBackend(); + ~QNetworkManagerNetworkInformationBackend() = default; + + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + if (!isValid()) + return {}; + return featuresSupportedStatic(); + } + + static QNetworkInformation::Features featuresSupportedStatic() + { + using Feature = QNetworkInformation::Feature; + return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal); + } + + bool isValid() const { return iface.isValid(); } + +private: + Q_DISABLE_COPY_MOVE(QNetworkManagerNetworkInformationBackend) + + QNetworkManagerInterface iface; +}; + +class QNetworkManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid) + Q_INTERFACES(QNetworkInformationBackendFactory) +public: + QNetworkManagerNetworkInformationBackendFactory() = default; + ~QNetworkManagerNetworkInformationBackendFactory() = default; + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + if (!QNetworkManagerInterfaceBase::networkManagerAvailable()) + return {}; + return QNetworkManagerNetworkInformationBackend::featuresSupportedStatic(); + } + + QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const override + { + if ((requiredFeatures & featuresSupported()) != requiredFeatures) + return nullptr; + if (!QNetworkManagerInterfaceBase::networkManagerAvailable()) + return nullptr; + auto backend = new QNetworkManagerNetworkInformationBackend(); + if (!backend->isValid()) + delete std::exchange(backend, nullptr); + return backend; + } +private: + Q_DISABLE_COPY_MOVE(QNetworkManagerNetworkInformationBackendFactory) +}; + +QNetworkManagerNetworkInformationBackend::QNetworkManagerNetworkInformationBackend() +{ + using NMState = QNetworkManagerInterface::NMState; + setReachability(reachabilityFromNMState(iface.state())); + connect(&iface, &QNetworkManagerInterface::stateChanged, this, + [this](NMState newState) { + setReachability(reachabilityFromNMState(newState)); + }); + + using ConnectivityState = QNetworkManagerInterface::NMConnectivityState; + + const auto connectivityState = iface.connectivityState(); + const bool behindPortal = (connectivityState == ConnectivityState::NM_CONNECTIVITY_PORTAL); + setBehindCaptivePortal(behindPortal); + + connect(&iface, &QNetworkManagerInterface::connectivityChanged, this, + [this](ConnectivityState state) { + const bool behindPortal = (state == ConnectivityState::NM_CONNECTIVITY_PORTAL); + setBehindCaptivePortal(behindPortal); + }); +} + +QT_END_NAMESPACE + +#include "qnetworkmanagernetworkinformationbackend.moc" diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp new file mode 100644 index 0000000000..764507fd4b --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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$ +** +****************************************************************************/ + +#include "qnetworkmanagerservice.h" + +#include <QObject> +#include <QList> +#include <QtDBus/QDBusConnection> +#include <QtDBus/QDBusError> +#include <QtDBus/QDBusInterface> +#include <QtDBus/QDBusMessage> +#include <QtDBus/QDBusReply> +#include <QtDBus/QDBusPendingCallWatcher> +#include <QtDBus/QDBusObjectPath> +#include <QtDBus/QDBusPendingCall> + +#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" + +QT_BEGIN_NAMESPACE + +QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent) + : QDBusAbstractInterface(QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), + NM_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) +{ +} + +bool QNetworkManagerInterfaceBase::networkManagerAvailable() +{ + return QNetworkManagerInterfaceBase().isValid(); +} + +QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent) + : QNetworkManagerInterfaceBase(parent) +{ + if (!isValid()) + return; + + PropertiesDBusInterface managerPropertiesInterface( + QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), DBUS_PROPERTIES_INTERFACE, + QDBusConnection::systemBus()); + QList<QVariant> argumentList; + argumentList << QLatin1String(NM_DBUS_INTERFACE); + QDBusPendingReply<QVariantMap> propsReply = managerPropertiesInterface.callWithArgumentList( + QDBus::Block, QLatin1String("GetAll"), argumentList); + if (!propsReply.isError()) { + propertyMap = propsReply.value(); + } else { + qWarning() << "propsReply" << propsReply.error().message(); + } + + QDBusConnection::systemBus().connect( + QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), + QLatin1String(NM_DBUS_INTERFACE), QLatin1String("PropertiesChanged"), this, + SLOT(setProperties(QMap<QString, QVariant>))); +} + +QNetworkManagerInterface::~QNetworkManagerInterface() +{ + QDBusConnection::systemBus().disconnect( + QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), + QLatin1String(NM_DBUS_INTERFACE), QLatin1String("PropertiesChanged"), this, + SLOT(setProperties(QMap<QString, QVariant>))); +} + +QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const +{ + if (propertyMap.contains("State")) + return static_cast<QNetworkManagerInterface::NMState>(propertyMap.value("State").toUInt()); + return QNetworkManagerInterface::NM_STATE_UNKNOWN; +} + +QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connectivityState() const +{ + if (propertyMap.contains("Connectivity")) + return static_cast<NMConnectivityState>(propertyMap.value("Connectivity").toUInt()); + return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN; +} + +void QNetworkManagerInterface::setProperties(const QMap<QString, QVariant> &map) +{ + for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) { + const bool isState = i.key() == QLatin1String("State"); + const bool isConnectivity = i.key() == QLatin1String("Connectivity"); + bool stateUpdate = isState; + bool connectivityUpdate = isConnectivity; + + auto it = propertyMap.lowerBound(i.key()); + if (it != propertyMap.end() && it.key() == i.key()) { + stateUpdate &= (it.value() != i.value()); + connectivityUpdate &= (it.value() != i.value()); + *it = *i; + } else { + propertyMap.insert(it, i.key(), i.value()); + } + + if (stateUpdate) { + quint32 state = i.value().toUInt(); + Q_EMIT stateChanged(static_cast<NMState>(state)); + } else if (connectivityUpdate) { + quint32 state = i.value().toUInt(); + Q_EMIT connectivityChanged(static_cast<NMConnectivityState>(state)); + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h new file mode 100644 index 0000000000..57c5aed763 --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 QNETWORKMANAGERSERVICE_H +#define QNETWORKMANAGERSERVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtDBus/QDBusAbstractInterface> +#include <QtDBus/QDBusPendingCallWatcher> +#include <QtDBus/QDBusObjectPath> + +#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" + +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" + +// Matches 'NMDeviceState' from https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html +enum NMDeviceState { + NM_DEVICE_STATE_UNKNOWN = 0, + NM_DEVICE_STATE_UNMANAGED = 10, + NM_DEVICE_STATE_UNAVAILABLE = 20, + NM_DEVICE_STATE_DISCONNECTED = 30, + NM_DEVICE_STATE_PREPARE = 40, + NM_DEVICE_STATE_CONFIG = 50, + NM_DEVICE_STATE_NEED_AUTH = 60, + NM_DEVICE_STATE_IP_CONFIG = 70, + NM_DEVICE_STATE_ACTIVATED = 100, + NM_DEVICE_STATE_DEACTIVATING = 110, + NM_DEVICE_STATE_FAILED = 120 +}; + +QT_BEGIN_NAMESPACE + +// This tiny class exists for the purpose of seeing if NetworkManager is available without +// initializing everything the derived/full class needs. +class QNetworkManagerInterfaceBase : public QDBusAbstractInterface +{ + Q_OBJECT +public: + QNetworkManagerInterfaceBase(QObject *parent = nullptr); + ~QNetworkManagerInterfaceBase() = default; + + static bool networkManagerAvailable(); + +private: + Q_DISABLE_COPY_MOVE(QNetworkManagerInterfaceBase) +}; + +class QNetworkManagerInterface final : public QNetworkManagerInterfaceBase +{ + Q_OBJECT + +public: + // Matches 'NMState' from https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html + enum NMState { + NM_STATE_UNKNOWN = 0, + NM_STATE_ASLEEP = 10, + NM_STATE_DISCONNECTED = 20, + NM_STATE_DISCONNECTING = 30, + NM_STATE_CONNECTING = 40, + NM_STATE_CONNECTED_LOCAL = 50, + NM_STATE_CONNECTED_SITE = 60, + NM_STATE_CONNECTED_GLOBAL = 70 + }; + Q_ENUM(NMState); + // Matches 'NMConnectivityState' from + // https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMConnectivityState + enum NMConnectivityState { + NM_CONNECTIVITY_UNKNOWN = 0, + NM_CONNECTIVITY_NONE = 1, + NM_CONNECTIVITY_PORTAL = 2, + NM_CONNECTIVITY_LIMITED = 3, + NM_CONNECTIVITY_FULL = 4, + }; + Q_ENUM(NMConnectivityState); + + QNetworkManagerInterface(QObject *parent = nullptr); + ~QNetworkManagerInterface(); + + NMState state() const; + NMConnectivityState connectivityState() const; + +Q_SIGNALS: + void stateChanged(NMState); + void connectivityChanged(NMConnectivityState); + +private Q_SLOTS: + void setProperties(const QMap<QString, QVariant> &map); + +private: + Q_DISABLE_COPY_MOVE(QNetworkManagerInterface) + + QVariantMap propertyMap; +}; + +class PropertiesDBusInterface : public QDBusAbstractInterface +{ +public: + PropertiesDBusInterface(const QString &service, const QString &path, const QString &interface, + const QDBusConnection &connection, QObject *parent = 0) + : QDBusAbstractInterface(service, path, interface.toLatin1().data(), connection, parent) + { + } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt new file mode 100644 index 0000000000..87ca1f0a6d --- /dev/null +++ b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt @@ -0,0 +1,11 @@ +qt_internal_add_plugin(QSCNetworkReachabilityNetworkInformationPlugin + OUTPUT_NAME scnetworkreachability + CLASS_NAME QSCNetworkReachabilityNetworkInformationBackendFactory + TYPE networkinformation + DEFAULT_IF APPLE + SOURCES + qscnetworkreachabilitynetworkinformationbackend.mm + PUBLIC_LIBRARIES + Qt::NetworkPrivate + ${FWSystemConfiguration} +) diff --git a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm new file mode 100644 index 0000000000..c732afc012 --- /dev/null +++ b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QtNetwork/private/qnetworkinformation_p.h> + +#include <QtNetwork/private/qnetconmonitor_p.h> + +#include <QtCore/qglobal.h> +#include <QtCore/private/qobject_p.h> + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR) +Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.scnetworkreachability"); + + +static QString backendName = QStringLiteral("scnetworkreachability"); + +class QSCNetworkReachabilityNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QSCNetworkReachabilityNetworkInformationBackend(); + ~QSCNetworkReachabilityNetworkInformationBackend(); + + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return featuresSupportedStatic(); + } + + static QNetworkInformation::Features featuresSupportedStatic() + { + return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability); + } + +private Q_SLOTS: + void reachabilityChanged(bool isOnline); + +private: + Q_DISABLE_COPY_MOVE(QSCNetworkReachabilityNetworkInformationBackend); + + QNetworkConnectionMonitor ipv4Probe; + QNetworkConnectionMonitor ipv6Probe; +}; + +class QSCNetworkReachabilityNetworkInformationBackendFactory : public QNetworkInformationBackendFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid) + Q_INTERFACES(QNetworkInformationBackendFactory) +public: + QSCNetworkReachabilityNetworkInformationBackendFactory() = default; + ~QSCNetworkReachabilityNetworkInformationBackendFactory() = default; + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return QSCNetworkReachabilityNetworkInformationBackend::featuresSupportedStatic(); + } + + QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const override + { + if ((requiredFeatures & featuresSupported()) != requiredFeatures) + return nullptr; + return new QSCNetworkReachabilityNetworkInformationBackend(); + } + +private: + Q_DISABLE_COPY_MOVE(QSCNetworkReachabilityNetworkInformationBackendFactory); +}; + +QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkInformationBackend() +{ + bool isOnline = false; + if (ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) { + // We manage to create SCNetworkReachabilityRef for IPv4, let's + // read the last known state then! + isOnline |= ipv4Probe.isReachable(); + ipv4Probe.startMonitoring(); + } + + if (ipv6Probe.setTargets(QHostAddress::AnyIPv6, {})) { + // We manage to create SCNetworkReachability ref for IPv6, let's + // read the last known state then! + isOnline |= ipv6Probe.isReachable(); + ipv6Probe.startMonitoring(); + } + reachabilityChanged(isOnline); + + connect(&ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, + &QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged, + Qt::QueuedConnection); + connect(&ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, + &QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged, + Qt::QueuedConnection); +} + +QSCNetworkReachabilityNetworkInformationBackend::~QSCNetworkReachabilityNetworkInformationBackend() +{ +} + +void QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged(bool isOnline) +{ + setReachability(isOnline ? QNetworkInformation::Reachability::Online + : QNetworkInformation::Reachability::Disconnected); +} + +QT_END_NAMESPACE + +#include "qscnetworkreachabilitynetworkinformationbackend.moc" |