diff options
Diffstat (limited to 'src/plugins/networkinformation')
22 files changed, 1280 insertions, 757 deletions
diff --git a/src/plugins/networkinformation/CMakeLists.txt b/src/plugins/networkinformation/CMakeLists.txt index 142031c62a..04da816264 100644 --- a/src/plugins/networkinformation/CMakeLists.txt +++ b/src/plugins/networkinformation/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + if(WIN32 AND QT_FEATURE_networklistmanager) add_subdirectory(networklistmanager) endif() @@ -13,3 +16,7 @@ endif() if(ANDROID) add_subdirectory(android) endif() + +if(QT_FEATURE_glib AND TARGET GLIB2::GOBJECT AND TARGET GLIB2::GIO) + add_subdirectory(glib) +endif() diff --git a/src/plugins/networkinformation/android/CMakeLists.txt b/src/plugins/networkinformation/android/CMakeLists.txt index f1d260547a..07d9201bbb 100644 --- a/src/plugins/networkinformation/android/CMakeLists.txt +++ b/src/plugins/networkinformation/android/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + set(java_sources jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java @@ -9,23 +12,23 @@ qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend OUTPUT_DIR "${QT_BUILD_DIR}/jar" ) +qt_path_join(destination ${INSTALL_DATADIR} "jar") + install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend - DESTINATION jar + DESTINATION ${destination} COMPONENT Devel ) qt_internal_add_plugin(QAndroidNetworkInformationPlugin - OUTPUT_NAME androidnetworkinformation + OUTPUT_NAME qandroidnetworkinformation CLASS_NAME QAndroidNetworkInformationBackendFactory - TYPE networkinformation + PLUGIN_TYPE networkinformation DEFAULT_IF ANDROID SOURCES qandroidnetworkinformationbackend.cpp wrapper/androidconnectivitymanager.cpp wrapper/androidconnectivitymanager.h LIBRARIES Qt::NetworkPrivate - DEFINES - QT_USE_QSTRINGBUILDER ) set_property( diff --git a/src/plugins/networkinformation/android/jar/.gitignore b/src/plugins/networkinformation/android/jar/.gitignore new file mode 100644 index 0000000000..364420a59a --- /dev/null +++ b/src/plugins/networkinformation/android/jar/.gitignore @@ -0,0 +1,6 @@ +.gradle/ +build/ +gradle/ +gradlew +gradlew.bat +local.properties diff --git a/src/plugins/networkinformation/android/jar/build.gradle b/src/plugins/networkinformation/android/jar/build.gradle new file mode 100644 index 0000000000..ea6d06c257 --- /dev/null +++ b/src/plugins/networkinformation/android/jar/build.gradle @@ -0,0 +1,51 @@ +// This is mainly used to allow Android Studio to easily read this folder as an android project. + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.4.0' + } +} + +apply plugin: 'com.android.library' + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) +} + +repositories { + google() + mavenCentral() +} + +android { + compileSdk 34 + + defaultConfig { + minSdkVersion 28 + } + + sourceSets { + main { + java.srcDir 'src/' + resources.srcDir 'libs/' + manifest.srcFile 'AndroidManifest.xml' + res.srcDirs = ['res/'] + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + android { + lintOptions { + abortOnError true + } + } +} diff --git a/src/plugins/networkinformation/android/jar/settings.gradle b/src/plugins/networkinformation/android/jar/settings.gradle new file mode 100644 index 0000000000..cbb1ff361b --- /dev/null +++ b/src/plugins/networkinformation/android/jar/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "QtAndroidNetworkInformationBackend" 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 index 0e89d23ab4..fc3e00311d 100644 --- 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 @@ -1,69 +1,47 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 package org.qtproject.qt.android.networkinformation; -import android.content.BroadcastReceiver; +import android.annotation.SuppressLint; 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; +import android.os.Build; -public class QtAndroidNetworkInformation { +class QtAndroidNetworkInformation { private static final String LOG_TAG = "QtAndroidNetworkInformation"; - private static native void connectivityChanged(); - private static native void behindCaptivePortalChanged(boolean state); + private static native void networkConnectivityChanged(int connectivity); + private static native void genericInfoChanged(boolean captivePortal, boolean metered); + private static native void transportMediumChanged(int transportMedium); private static QtNetworkInformationCallback m_callback = null; private static final Object m_lock = new Object(); + // Keep synchronized with AndroidConnectivity in androidconnectivitymanager.h enum AndroidConnectivity { Connected, Unknown, Disconnected } + // Keep synchronized with AndroidTransport in androidconnectivitymanager.h + enum Transport { + Unknown, + Bluetooth, + Cellular, + Ethernet, + LoWPAN, + Usb, + WiFi, + WiFiAware, + } + private static class QtNetworkInformationCallback extends NetworkCallback { - public AndroidConnectivity previousState = null; + AndroidConnectivity previousState = null; + Transport previousTransport = null; QtNetworkInformationCallback() { } @@ -77,17 +55,55 @@ public class QtAndroidNetworkInformation { s = AndroidConnectivity.Connected; else s = AndroidConnectivity.Unknown; // = we _may_ have Internet access + + final Transport transport = getTransport(capabilities); + if (transport == Transport.Unknown) // If we don't have any transport media: override + s = AndroidConnectivity.Unknown; + setState(s); + setTransportMedium(transport); final boolean captive = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); - behindCaptivePortalChanged(captive); + final boolean metered + = !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + genericInfoChanged(captive, metered); + } + + private Transport getTransport(NetworkCapabilities capabilities) + { + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return Transport.WiFi; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return Transport.Cellular; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) { + return Transport.Bluetooth; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + return Transport.Ethernet; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) { + // Build.VERSION_CODES.O + return Transport.WiFiAware; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)) { + // Build.VERSION_CODES.O_MR1 + return Transport.LoWPAN; + }/* else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB)) { + // Build.VERSION_CODES.S + return Transport.Usb; + }*/ // @todo: Uncomment once we can use SDK 31 + return Transport.Unknown; } private void setState(AndroidConnectivity s) { if (previousState != s) { previousState = s; - connectivityChanged(); + networkConnectivityChanged(s.ordinal()); + } + } + + private void setTransportMedium(Transport t) { + if (previousTransport != t) { + previousTransport = t; + transportMediumChanged(t.ordinal()); } } @@ -100,28 +116,36 @@ public class QtAndroidNetworkInformation { private QtAndroidNetworkInformation() { } - public static AndroidConnectivity state() { + 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) { + @SuppressLint("MissingPermission") + 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(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) + builder = builder.clearCapabilities(); builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND); + } NetworkRequest request = builder.build(); + + // Can't use registerDefaultNetworkCallback because it doesn't let us know when + // the network disconnects! manager.registerNetworkCallback(request, m_callback); } } } - public static void unregisterReceiver(final Context context) { + static void unregisterReceiver(final Context context) { synchronized (m_lock) { if (m_callback != null) { getConnectivityManager(context).unregisterNetworkCallback(m_callback); @@ -130,7 +154,7 @@ public class QtAndroidNetworkInformation { } } - public static ConnectivityManager getConnectivityManager(final Context context) { + 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 index 9eef471989..44e4d447d2 100644 --- a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp +++ b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.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 #include <QtNetwork/private/qnetworkinformation_p.h> @@ -47,7 +11,10 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcNetInfoAndroid) Q_LOGGING_CATEGORY(lcNetInfoAndroid, "qt.network.info.android"); -static const QString backendName = QStringLiteral("android"); +static QString backendName() { + return QString::fromUtf16(QNetworkInformationBackend::PluginNames + [QNetworkInformationBackend::PluginNamesAndroidIndex]); +} class QAndroidNetworkInformationBackend : public QNetworkInformationBackend { @@ -56,7 +23,7 @@ public: QAndroidNetworkInformationBackend(); ~QAndroidNetworkInformationBackend() { m_valid = false; } - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return featuresSupportedStatic(); @@ -65,13 +32,18 @@ public: static QNetworkInformation::Features featuresSupportedStatic() { using Feature = QNetworkInformation::Feature; - return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal); + return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal + | Feature::TransportMedium); } bool isValid() { return m_valid; } private: Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend); + + void updateConnectivity(AndroidConnectivityManager::AndroidConnectivity connectivity); + void updateTransportMedium(AndroidConnectivityManager::AndroidTransport transport); + bool m_valid = false; }; @@ -83,7 +55,7 @@ class QAndroidNetworkInformationBackendFactory : public QNetworkInformationBacke public: QAndroidNetworkInformationBackendFactory() = default; ~QAndroidNetworkInformationBackendFactory() = default; - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return QAndroidNetworkInformationBackend::featuresSupportedStatic(); @@ -111,24 +83,63 @@ QAndroidNetworkInformationBackend::QAndroidNetworkInformationBackend() 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::connectivityChanged, this, + &QAndroidNetworkInformationBackend::updateConnectivity); connect(conman, &AndroidConnectivityManager::captivePortalChanged, this, &QAndroidNetworkInformationBackend::setBehindCaptivePortal); + + connect(conman, &AndroidConnectivityManager::transportMediumChanged, this, + &QAndroidNetworkInformationBackend::updateTransportMedium); + + connect(conman, &AndroidConnectivityManager::meteredChanged, this, + &QAndroidNetworkInformationBackend::setMetered); +} + +void QAndroidNetworkInformationBackend::updateConnectivity( + AndroidConnectivityManager::AndroidConnectivity connectivity) +{ + using AndroidConnectivity = AndroidConnectivityManager::AndroidConnectivity; + static const auto mapState = [](AndroidConnectivity state) { + switch (state) { + case AndroidConnectivity::Connected: + return QNetworkInformation::Reachability::Online; + case AndroidConnectivity::Disconnected: + return QNetworkInformation::Reachability::Disconnected; + case AndroidConnectivity::Unknown: + default: + return QNetworkInformation::Reachability::Unknown; + } + }; + + setReachability(mapState(connectivity)); +} + +void QAndroidNetworkInformationBackend::updateTransportMedium( + AndroidConnectivityManager::AndroidTransport transport) +{ + using AndroidTransport = AndroidConnectivityManager::AndroidTransport; + using TransportMedium = QNetworkInformation::TransportMedium; + static const auto mapTransport = [](AndroidTransport state) -> TransportMedium { + switch (state) { + case AndroidTransport::Cellular: + return TransportMedium::Cellular; + case AndroidTransport::WiFi: + return TransportMedium::WiFi; + case AndroidTransport::Bluetooth: + return TransportMedium::Bluetooth; + case AndroidTransport::Ethernet: + return TransportMedium::Ethernet; + // These are not covered yet (but may be in the future) + case AndroidTransport::Usb: + case AndroidTransport::LoWPAN: + case AndroidTransport::WiFiAware: + case AndroidTransport::Unknown: + return TransportMedium::Unknown; + } + }; + + setTransportMedium(mapTransport(transport)); } QT_END_NAMESPACE diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp index e88fe7d955..3c9f952968 100644 --- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 #include "androidconnectivitymanager.h" @@ -56,34 +20,43 @@ Q_GLOBAL_STATIC(AndroidConnectivityManagerInstance, androidConnManagerInstance) static const char networkInformationClass[] = "org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation"; -static void networkConnectivityChanged(JNIEnv *env, jobject obj) +static void networkConnectivityChanged(JNIEnv *env, jobject obj, jint enumValue) { Q_UNUSED(env); Q_UNUSED(obj); - Q_EMIT androidConnManagerInstance->connManager->connectivityChanged(); + const auto connectivity = + static_cast<AndroidConnectivityManager::AndroidConnectivity>(enumValue); + Q_EMIT androidConnManagerInstance->connManager->connectivityChanged(connectivity); } +Q_DECLARE_JNI_NATIVE_METHOD(networkConnectivityChanged) -static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean state) +static void genericInfoChanged(JNIEnv *env, jobject obj, jboolean captivePortal, jboolean metered) { Q_UNUSED(env); Q_UNUSED(obj); - Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state); + Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(captivePortal); + Q_EMIT androidConnManagerInstance->connManager->meteredChanged(metered); } +Q_DECLARE_JNI_NATIVE_METHOD(genericInfoChanged) + +static void transportMediumChanged(JNIEnv *env, jobject obj, jint enumValue) +{ + Q_UNUSED(env); + Q_UNUSED(obj); + const auto transport = static_cast<AndroidConnectivityManager::AndroidTransport>(enumValue); + emit androidConnManagerInstance->connManager->transportMediumChanged(transport); +} +Q_DECLARE_JNI_NATIVE_METHOD(transportMediumChanged) + +Q_DECLARE_JNI_CLASS(ConnectivityManager, "android/net/ConnectivityManager") 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()); + QAndroidApplication::context()); } AndroidConnectivityManager *AndroidConnectivityManager::getInstance() @@ -95,52 +68,30 @@ AndroidConnectivityManager *AndroidConnectivityManager::getInstance() : nullptr; } -AndroidConnectivityManager::~AndroidConnectivityManager() +bool AndroidConnectivityManager::isValid() const { - QJniObject::callStaticMethod<void>(networkInformationClass, "unregisterReceiver", - "(Landroid/content/Context;)V", QAndroidApplication::context()); + return registerNatives(); } -AndroidConnectivityManager::AndroidConnectivity AndroidConnectivityManager::networkConnectivity() +AndroidConnectivityManager::~AndroidConnectivityManager() { - 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; + QJniObject::callStaticMethod<void>(networkInformationClass, "unregisterReceiver", + QAndroidApplication::context()); } -bool AndroidConnectivityManager::registerNatives() +bool AndroidConnectivityManager::registerNatives() const { - 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; + static const bool registered = []() { + QJniEnvironment env; + return env.registerNativeMethods(networkInformationClass, { + Q_JNI_NATIVE_METHOD(networkConnectivityChanged), + Q_JNI_NATIVE_METHOD(genericInfoChanged), + Q_JNI_NATIVE_METHOD(transportMediumChanged), + }); + }(); + return registered; } QT_END_NAMESPACE + +#include "moc_androidconnectivitymanager.cpp" diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h index 14f5aa9b57..d15faf0e8e 100644 --- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 #ifndef ANDROIDCONNECTIVITYMANAGER_H #define ANDROIDCONNECTIVITYMANAGER_H @@ -49,23 +13,38 @@ class AndroidConnectivityManager : public QObject { Q_OBJECT public: + // Keep synchronized with AndroidConnectivity in QtAndroidNetworkInformation.java enum class AndroidConnectivity { Connected, Unknown, Disconnected }; Q_ENUM(AndroidConnectivity); + + // Keep synchronized with Transport in QtAndroidNetworkInformation.java + enum class AndroidTransport { + Unknown, + Bluetooth, + Cellular, + Ethernet, + LoWPAN, + Usb, + WiFi, + WiFiAware, + }; + Q_ENUM(AndroidTransport); + static AndroidConnectivityManager *getInstance(); ~AndroidConnectivityManager(); - AndroidConnectivity networkConnectivity(); - inline bool isValid() const { return m_connectivityManager.isValid(); } + inline bool isValid() const; Q_SIGNALS: - void connectivityChanged(); + void connectivityChanged(AndroidConnectivity connectivity); void captivePortalChanged(bool state); + void transportMediumChanged(AndroidTransport transport); + void meteredChanged(bool state); private: friend struct AndroidConnectivityManagerInstance; AndroidConnectivityManager(); - bool registerNatives(); - QJniObject m_connectivityManager; + bool registerNatives() const; Q_DISABLE_COPY_MOVE(AndroidConnectivityManager); }; diff --git a/src/plugins/networkinformation/glib/CMakeLists.txt b/src/plugins/networkinformation/glib/CMakeLists.txt new file mode 100644 index 0000000000..019f4f1358 --- /dev/null +++ b/src/plugins/networkinformation/glib/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_plugin(QGlibNetworkInformationPlugin + OUTPUT_NAME qglib + CLASS_NAME QGlibNetworkInformationBackendFactory + PLUGIN_TYPE networkinformation + DEFAULT_IF LINUX + SOURCES + qglibnetworkinformationbackend.cpp + LIBRARIES + Qt::NetworkPrivate + GLIB2::GOBJECT + GLIB2::GIO + DEFINES + QT_NO_SIGNALS_SLOTS_KEYWORDS +) diff --git a/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp new file mode 100644 index 0000000000..0b45eb9ce3 --- /dev/null +++ b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp @@ -0,0 +1,137 @@ +// Copyright (C) 2021 Ilya Fedin <fedin-ilja2010@ya.ru> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QtNetwork/private/qnetworkinformation_p.h> + +#include <QtCore/qglobal.h> +#include <QtCore/private/qobject_p.h> + +#include <gio/gio.h> + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoGlib) +Q_LOGGING_CATEGORY(lcNetInfoGlib, "qt.network.info.glib"); + +namespace { +QNetworkInformation::Reachability reachabilityFromGNetworkConnectivity(GNetworkConnectivity connectivity) +{ + switch (connectivity) { + case G_NETWORK_CONNECTIVITY_LOCAL: + return QNetworkInformation::Reachability::Disconnected; + case G_NETWORK_CONNECTIVITY_LIMITED: + case G_NETWORK_CONNECTIVITY_PORTAL: + return QNetworkInformation::Reachability::Site; + case G_NETWORK_CONNECTIVITY_FULL: + return QNetworkInformation::Reachability::Online; + } + return QNetworkInformation::Reachability::Unknown; +} +} + +static QString backendName = QStringLiteral("glib"); + +class QGlibNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QGlibNetworkInformationBackend(); + ~QGlibNetworkInformationBackend(); + + 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 + | Feature::Metered); + } + + bool isValid() const; + +private: + Q_DISABLE_COPY_MOVE(QGlibNetworkInformationBackend) + + static void updateConnectivity(QGlibNetworkInformationBackend *backend); + static void updateMetered(QGlibNetworkInformationBackend *backend); + + GNetworkMonitor *networkMonitor = nullptr; + gulong connectivityHandlerId = 0; + gulong meteredHandlerId = 0; +}; + +class QGlibNetworkInformationBackendFactory : public QNetworkInformationBackendFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid) + Q_INTERFACES(QNetworkInformationBackendFactory) +public: + QGlibNetworkInformationBackendFactory() = default; + ~QGlibNetworkInformationBackendFactory() = default; + QString name() const override { return backendName; } + QNetworkInformation::Features featuresSupported() const override + { + return QGlibNetworkInformationBackend::featuresSupportedStatic(); + } + + QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const override + { + if ((requiredFeatures & featuresSupported()) != requiredFeatures) + return nullptr; + auto backend = new QGlibNetworkInformationBackend(); + if (!backend->isValid()) + delete std::exchange(backend, nullptr); + return backend; + } +private: + Q_DISABLE_COPY_MOVE(QGlibNetworkInformationBackendFactory) +}; + +QGlibNetworkInformationBackend::QGlibNetworkInformationBackend() +: networkMonitor(g_network_monitor_get_default()) +{ + updateConnectivity(this); + updateMetered(this); + + connectivityHandlerId = g_signal_connect_swapped(networkMonitor, "notify::connectivity", + G_CALLBACK(updateConnectivity), this); + + meteredHandlerId = g_signal_connect_swapped(networkMonitor, "notify::network-metered", + G_CALLBACK(updateMetered), this); +} + +QGlibNetworkInformationBackend::~QGlibNetworkInformationBackend() +{ + g_signal_handler_disconnect(networkMonitor, meteredHandlerId); + g_signal_handler_disconnect(networkMonitor, connectivityHandlerId); +} + +bool QGlibNetworkInformationBackend::isValid() const +{ + return QLatin1StringView(G_OBJECT_TYPE_NAME(networkMonitor)) != "GNetworkMonitorBase"_L1; +} + +void QGlibNetworkInformationBackend::updateConnectivity(QGlibNetworkInformationBackend *backend) +{ + const auto connectivityState = g_network_monitor_get_connectivity(backend->networkMonitor); + const bool behindPortal = (connectivityState == G_NETWORK_CONNECTIVITY_PORTAL); + backend->setReachability(reachabilityFromGNetworkConnectivity(connectivityState)); + backend->setBehindCaptivePortal(behindPortal); +} + +void QGlibNetworkInformationBackend::updateMetered(QGlibNetworkInformationBackend *backend) +{ + backend->setMetered(g_network_monitor_get_network_metered(backend->networkMonitor)); +} + +QT_END_NAMESPACE + +#include "qglibnetworkinformationbackend.moc" diff --git a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt index 1ffdfdec17..acd3754f4e 100644 --- a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt +++ b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt @@ -1,13 +1,25 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QNLMNIPlugin - OUTPUT_NAME networklistmanager + OUTPUT_NAME qnetworklistmanager CLASS_NAME QNetworkListManagerNetworkInformationBackendFactory - TYPE networkinformation + PLUGIN_TYPE networkinformation DEFAULT_IF WIN32 AND QT_FEATURE_networklistmanager - SOURCES qnetworklistmanagernetworkinformationbackend.cpp + EXCEPTIONS + SOURCES + qnetworklistmanagernetworkinformationbackend.cpp + qnetworklistmanagerevents.h qnetworklistmanagerevents.cpp LIBRARIES Qt::NetworkPrivate ) +qt_internal_extend_target(QNLMNIPlugin CONDITION WIN32 + LIBRARIES + runtimeobject + oleaut32 +) + # 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/qnetworklistmanagerevents.cpp b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp new file mode 100644 index 0000000000..caa5046751 --- /dev/null +++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp @@ -0,0 +1,268 @@ +// 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 + +#include "qnetworklistmanagerevents.h" +#include <QtCore/private/qsystemerror_p.h> + +#include <QtCore/qpointer.h> + +#include <mutex> + +#if QT_CONFIG(cpp_winrt) +#include <QtCore/private/qt_winrtbase_p.h> + +#include <winrt/Windows.Networking.Connectivity.h> +#endif // QT_CONFIG(cpp_winrt) + +QT_BEGIN_NAMESPACE + +namespace { +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; +} +} + +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:" + << QSystemError::windowsComString(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:" + << QSystemError::windowsComString(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 + emit 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:" + << QSystemError::windowsComString(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:" + << QSystemError::windowsComString(hr); + } else { + emit connectivityChanged(connectivity); + } + +#if QT_CONFIG(cpp_winrt) + using namespace winrt::Windows::Networking::Connectivity; + using winrt::Windows::Foundation::IInspectable; + try { + // Register for changes in the network and store a token to unregister later: + token = NetworkInformation::NetworkStatusChanged( + [owner = QPointer(this)](const IInspectable sender) { + Q_UNUSED(sender); + if (owner) { + std::scoped_lock locker(owner->winrtLock); + if (owner->token) + owner->emitWinRTUpdates(); + } + }); + } catch (const winrt::hresult_error &ex) { + qCWarning(lcNetInfoNLM) << "Failed to register network status changed callback:" + << QSystemError::windowsComString(ex.code()); + } + + // Emit initial state + emitWinRTUpdates(); +#endif + + return true; +} + +void QNetworkListManagerEvents::stop() +{ + Q_ASSERT(connectionPoint); + auto hr = connectionPoint->Unadvise(cookie); + if (FAILED(hr)) { + qCWarning(lcNetInfoNLM) << "Failed to unsubscribe from network connectivity events:" + << QSystemError::windowsComString(hr); + } else { + cookie = 0; + } + // Even if we fail we should still try to unregister from winrt events: + +#if QT_CONFIG(cpp_winrt) + // Try to synchronize unregistering with potentially in-progress callbacks + std::scoped_lock locker(winrtLock); + if (token) { + using namespace winrt::Windows::Networking::Connectivity; + // Pass the token we stored earlier to unregister: + NetworkInformation::NetworkStatusChanged(token); + token = {}; + } +#endif +} + +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[] = { L"NA_InternetConnectivityV6", L"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; +} + +#if QT_CONFIG(cpp_winrt) +namespace { +using namespace winrt::Windows::Networking::Connectivity; +// NB: this isn't part of "network list manager", but sadly NLM doesn't have an +// equivalent API (at least not that I've found...)! +[[nodiscard]] +QNetworkInformation::TransportMedium getTransportMedium(const ConnectionProfile &profile) +{ + if (profile.IsWwanConnectionProfile()) + return QNetworkInformation::TransportMedium::Cellular; + if (profile.IsWlanConnectionProfile()) + return QNetworkInformation::TransportMedium::WiFi; + + NetworkAdapter adapter(nullptr); + try { + adapter = profile.NetworkAdapter(); + } catch (const winrt::hresult_error &ex) { + qCWarning(lcNetInfoNLM) << "Failed to obtain network adapter:" + << QSystemError::windowsComString(ex.code()); + // pass, we will return Unknown anyway + } + if (adapter == nullptr) + return QNetworkInformation::TransportMedium::Unknown; + + // Note: Bluetooth is given an iana iftype of 6, which is the same as Ethernet. + // In Windows itself there is clearly a distinction between a Bluetooth PAN + // and an Ethernet LAN, though it is not clear how they make this distinction. + auto fromIanaId = [](quint32 ianaId) -> QNetworkInformation::TransportMedium { + // https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib + switch (ianaId) { + case 6: + return QNetworkInformation::TransportMedium::Ethernet; + case 71: // Should be handled before entering this lambda + return QNetworkInformation::TransportMedium::WiFi; + } + return QNetworkInformation::TransportMedium::Unknown; + }; + + return fromIanaId(adapter.IanaInterfaceType()); +} + +[[nodiscard]] bool getMetered(const ConnectionProfile &profile) +{ + ConnectionCost cost(nullptr); + try { + cost = profile.GetConnectionCost(); + } catch (const winrt::hresult_error &ex) { + qCWarning(lcNetInfoNLM) << "Failed to obtain connection cost:" + << QSystemError::windowsComString(ex.code()); + // pass, we return false if we get an empty object back anyway + } + if (cost == nullptr) + return false; + NetworkCostType type = cost.NetworkCostType(); + return type == NetworkCostType::Fixed || type == NetworkCostType::Variable; +} +} // unnamed namespace + +void QNetworkListManagerEvents::emitWinRTUpdates() +{ + using namespace winrt::Windows::Networking::Connectivity; + ConnectionProfile profile = nullptr; + try { + profile = NetworkInformation::GetInternetConnectionProfile(); + } catch (const winrt::hresult_error &ex) { + qCWarning(lcNetInfoNLM) << "Failed to obtain connection profile:" + << QSystemError::windowsComString(ex.code()); + // pass, we would just return early if we get an empty object back anyway + } + if (profile == nullptr) + return; + emit transportMediumChanged(getTransportMedium(profile)); + emit isMeteredChanged(getMetered(profile)); +} +#endif // QT_CONFIG(cpp_winrt) + +QT_END_NAMESPACE + +#include "moc_qnetworklistmanagerevents.cpp" diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h new file mode 100644 index 0000000000..d91cd8a4cc --- /dev/null +++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h @@ -0,0 +1,79 @@ +// 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 + +#ifndef QNETWORKLISTMANAGEREVENTS_H +#define QNETWORKLISTMANAGEREVENTS_H + +#include <QtNetwork/private/qtnetworkglobal_p.h> + +#include <QtNetwork/qnetworkinformation.h> + +#include <QtCore/qstring.h> +#include <QtCore/qobject.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qmutex.h> + +#include <objbase.h> +#include <ocidl.h> +#include <netlistmgr.h> +#include <wrl/client.h> +#include <wrl/wrappers/corewrappers.h> + +#if QT_CONFIG(cpp_winrt) +#include <QtCore/private/qt_winrtbase_p.h> +#endif + +using namespace Microsoft::WRL; + +QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcNetInfoNLM) + +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(); + void stop(); + + [[nodiscard]] bool checkBehindCaptivePortal(); + +signals: + void connectivityChanged(NLM_CONNECTIVITY); + void transportMediumChanged(QNetworkInformation::TransportMedium); + void isMeteredChanged(bool); + +private: + ComPtr<INetworkListManager> networkListManager = nullptr; + ComPtr<IConnectionPoint> connectionPoint = nullptr; + +#if QT_CONFIG(cpp_winrt) + void emitWinRTUpdates(); + + winrt::event_token token; + QMutex winrtLock; +#endif + + QAtomicInteger<ULONG> ref = 0; + DWORD cookie = 0; +}; + +QT_END_NAMESPACE + +#endif // QNETWORKLISTMANAGEREVENTS_H diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp index d37f83832b..766648486e 100644 --- a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp +++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp @@ -1,80 +1,28 @@ -/**************************************************************************** -** -** 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 #include <QtNetwork/private/qnetworkinformation_p.h> +#include "qnetworklistmanagerevents.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; +#include <QtCore/private/qfunctions_win_p.h> 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()); -} +// Declared in qnetworklistmanagerevents.h +Q_LOGGING_CATEGORY(lcNetInfoNLM, "qt.network.info.netlistmanager"); -template<typename T> -bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject) +static QString backendName() { - if (riid == __uuidof(T)) { - *ppvObject = static_cast<T *>(from); - from->AddRef(); - return true; - } - return false; + return QString::fromUtf16(QNetworkInformationBackend::PluginNames + [QNetworkInformationBackend::PluginNamesWindowsIndex]); } +namespace { bool testCONNECTIVITY(NLM_CONNECTIVITY connectivity, NLM_CONNECTIVITY flag) { return (connectivity & flag) == flag; @@ -105,7 +53,6 @@ QNetworkInformation::Reachability reachabilityFromNLM_CONNECTIVITY(NLM_CONNECTIV } } -class QNetworkListManagerEvents; class QNetworkListManagerNetworkInformationBackend : public QNetworkInformationBackend { Q_OBJECT @@ -113,7 +60,7 @@ public: QNetworkListManagerNetworkInformationBackend(); ~QNetworkListManagerNetworkInformationBackend(); - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return featuresSupportedStatic(); @@ -122,25 +69,29 @@ public: static QNetworkInformation::Features featuresSupportedStatic() { return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability - | QNetworkInformation::Feature::CaptivePortal); + | QNetworkInformation::Feature::CaptivePortal +#if QT_CONFIG(cpp_winrt) + | QNetworkInformation::Feature::TransportMedium + | QNetworkInformation::Feature::Metered +#endif + ); } [[nodiscard]] bool start(); void stop(); private: - friend class QNetworkListManagerEvents; - bool event(QEvent *event) override; void setConnectivity(NLM_CONNECTIVITY newConnectivity); void checkCaptivePortal(); + QComHelper comHelper; + ComPtr<QNetworkListManagerEvents> managerEvents; NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED; bool monitoring = false; - bool comInitFailed = false; }; class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory @@ -151,7 +102,7 @@ class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInfor public: QNetworkListManagerNetworkInformationBackendFactory() = default; ~QNetworkListManagerNetworkInformationBackendFactory() = default; - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return QNetworkListManagerNetworkInformationBackend::featuresSupportedStatic(); @@ -172,182 +123,24 @@ public: } }; -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; + if (!comHelper.isValid()) return; - } + managerEvents = new QNetworkListManagerEvents(); connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this, &QNetworkListManagerNetworkInformationBackend::setConnectivity); + + connect(managerEvents.Get(), &QNetworkListManagerEvents::transportMediumChanged, this, + &QNetworkListManagerNetworkInformationBackend::setTransportMedium); + + connect(managerEvents.Get(), &QNetworkListManagerEvents::isMeteredChanged, this, + &QNetworkListManagerNetworkInformationBackend::setMetered); } QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend() { - if (comInitFailed) - return; stop(); } @@ -370,11 +163,8 @@ void QNetworkListManagerNetworkInformationBackend::checkCaptivePortal() bool QNetworkListManagerNetworkInformationBackend::event(QEvent *event) { - if (event->type() == QEvent::ThreadChange && monitoring) { - stop(); - QMetaObject::invokeMethod(this, &QNetworkListManagerNetworkInformationBackend::start, - Qt::QueuedConnection); - } + if (event->type() == QEvent::ThreadChange) + qFatal("Moving QNetworkListManagerNetworkInformationBackend to different thread is not supported"); return QObject::event(event); } @@ -383,15 +173,9 @@ 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 (!comHelper.isValid()) + return false; + if (!managerEvents) managerEvents = new QNetworkListManagerEvents(); @@ -404,14 +188,10 @@ 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 diff --git a/src/plugins/networkinformation/networkmanager/CMakeLists.txt b/src/plugins/networkinformation/networkmanager/CMakeLists.txt index a80a7ac656..9d76dbe7b4 100644 --- a/src/plugins/networkinformation/networkmanager/CMakeLists.txt +++ b/src/plugins/networkinformation/networkmanager/CMakeLists.txt @@ -1,9 +1,13 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QNetworkManagerNetworkInformationPlugin - OUTPUT_NAME networkmanager + OUTPUT_NAME qnetworkmanager CLASS_NAME QNetworkManagerNetworkInformationBackendFactory - TYPE networkinformation + PLUGIN_TYPE networkinformation DEFAULT_IF LINUX SOURCES + qnetworkmanagernetworkinformationbackend.h qnetworkmanagernetworkinformationbackend.cpp qnetworkmanagerservice.h qnetworkmanagerservice.cpp diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp index bfb04ae4a6..f583d1dcf6 100644 --- a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp @@ -1,45 +1,7 @@ -/**************************************************************************** -** -** 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" +// 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 + +#include "qnetworkmanagernetworkinformationbackend.h" #include <QtCore/qglobal.h> #include <QtCore/private/qobject_p.h> @@ -70,38 +32,80 @@ QNetworkInformation::Reachability reachabilityFromNMState(QNetworkManagerInterfa } return QNetworkInformation::Reachability::Unknown; } -} -static QString backendName = QStringLiteral("networkmanager"); - -class QNetworkManagerNetworkInformationBackend : public QNetworkInformationBackend +QNetworkInformation::TransportMedium +transportMediumFromDeviceType(QNetworkManagerInterface::NMDeviceType type) { - Q_OBJECT -public: - QNetworkManagerNetworkInformationBackend(); - ~QNetworkManagerNetworkInformationBackend() = default; - - QString name() const override { return backendName; } - QNetworkInformation::Features featuresSupported() const override - { - if (!isValid()) - return {}; - return featuresSupportedStatic(); + switch (type) { + case QNetworkManagerInterface::NM_DEVICE_TYPE_ETHERNET: + return QNetworkInformation::TransportMedium::Ethernet; + case QNetworkManagerInterface::NM_DEVICE_TYPE_WIFI: + return QNetworkInformation::TransportMedium::WiFi; + case QNetworkManagerInterface::NM_DEVICE_TYPE_BT: + return QNetworkInformation::TransportMedium::Bluetooth; + case QNetworkManagerInterface::NM_DEVICE_TYPE_MODEM: + return QNetworkInformation::TransportMedium::Cellular; + + case QNetworkManagerInterface::NM_DEVICE_TYPE_UNKNOWN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_GENERIC: + case QNetworkManagerInterface::NM_DEVICE_TYPE_UNUSED1: + case QNetworkManagerInterface::NM_DEVICE_TYPE_UNUSED2: + case QNetworkManagerInterface::NM_DEVICE_TYPE_OLPC_MESH: + case QNetworkManagerInterface::NM_DEVICE_TYPE_WIMAX: + case QNetworkManagerInterface::NM_DEVICE_TYPE_INFINIBAND: + case QNetworkManagerInterface::NM_DEVICE_TYPE_BOND: + case QNetworkManagerInterface::NM_DEVICE_TYPE_VLAN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_ADSL: + case QNetworkManagerInterface::NM_DEVICE_TYPE_BRIDGE: + case QNetworkManagerInterface::NM_DEVICE_TYPE_TEAM: + case QNetworkManagerInterface::NM_DEVICE_TYPE_TUN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_IP_TUNNEL: + case QNetworkManagerInterface::NM_DEVICE_TYPE_MACVLAN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_VXLAN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_VETH: + case QNetworkManagerInterface::NM_DEVICE_TYPE_MACSEC: + case QNetworkManagerInterface::NM_DEVICE_TYPE_DUMMY: + case QNetworkManagerInterface::NM_DEVICE_TYPE_PPP: + case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_INTERFACE: + case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_PORT: + case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_BRIDGE: + case QNetworkManagerInterface::NM_DEVICE_TYPE_WPAN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_6LOWPAN: + case QNetworkManagerInterface::NM_DEVICE_TYPE_WIREGUARD: + case QNetworkManagerInterface::NM_DEVICE_TYPE_WIFI_P2P: + case QNetworkManagerInterface::NM_DEVICE_TYPE_VRF: + break; } + // While the list is exhaustive of the enum there can be additional + // entries added in NetworkManager that isn't listed here + return QNetworkInformation::TransportMedium::Unknown; +} - static QNetworkInformation::Features featuresSupportedStatic() - { - using Feature = QNetworkInformation::Feature; - return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal); +bool isMeteredFromNMMetered(QNetworkManagerInterface::NMMetered metered) +{ + switch (metered) { + case QNetworkManagerInterface::NM_METERED_YES: + case QNetworkManagerInterface::NM_METERED_GUESS_YES: + return true; + case QNetworkManagerInterface::NM_METERED_NO: + case QNetworkManagerInterface::NM_METERED_GUESS_NO: + case QNetworkManagerInterface::NM_METERED_UNKNOWN: + return false; } + Q_UNREACHABLE_RETURN(false); +} +} // unnamed namespace - bool isValid() const { return iface.isValid(); } - -private: - Q_DISABLE_COPY_MOVE(QNetworkManagerNetworkInformationBackend) +static QString backendName() +{ + return QStringView(QNetworkInformationBackend::PluginNames + [QNetworkInformationBackend::PluginNamesLinuxIndex]).toString(); +} - QNetworkManagerInterface iface; -}; +QString QNetworkManagerNetworkInformationBackend::name() const +{ + return backendName(); +} class QNetworkManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory { @@ -111,7 +115,7 @@ class QNetworkManagerNetworkInformationBackendFactory : public QNetworkInformati public: QNetworkManagerNetworkInformationBackendFactory() = default; ~QNetworkManagerNetworkInformationBackendFactory() = default; - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { if (!QNetworkManagerInterfaceBase::networkManagerAvailable()) @@ -136,26 +140,42 @@ private: QNetworkManagerNetworkInformationBackend::QNetworkManagerNetworkInformationBackend() { - using NMState = QNetworkManagerInterface::NMState; - setReachability(reachabilityFromNMState(iface.state())); - connect(&iface, &QNetworkManagerInterface::stateChanged, this, - [this](NMState newState) { - setReachability(reachabilityFromNMState(newState)); - }); + if (!iface.isValid()) + return; + iface.setBackend(this); + onStateChanged(iface.state()); + onConnectivityChanged(iface.connectivityState()); + onDeviceTypeChanged(iface.deviceType()); + onMeteredChanged(iface.meteredState()); +} - using ConnectivityState = QNetworkManagerInterface::NMConnectivityState; +void QNetworkManagerNetworkInformationBackend::onStateChanged( + QNetworkManagerInterface::NMState newState) +{ + setReachability(reachabilityFromNMState(newState)); +} - const auto connectivityState = iface.connectivityState(); - const bool behindPortal = (connectivityState == ConnectivityState::NM_CONNECTIVITY_PORTAL); +void QNetworkManagerNetworkInformationBackend::onConnectivityChanged( + QNetworkManagerInterface::NMConnectivityState connectivityState) +{ + const bool behindPortal = + (connectivityState == QNetworkManagerInterface::NM_CONNECTIVITY_PORTAL); setBehindCaptivePortal(behindPortal); +} - connect(&iface, &QNetworkManagerInterface::connectivityChanged, this, - [this](ConnectivityState state) { - const bool behindPortal = (state == ConnectivityState::NM_CONNECTIVITY_PORTAL); - setBehindCaptivePortal(behindPortal); - }); +void QNetworkManagerNetworkInformationBackend::onDeviceTypeChanged( + QNetworkManagerInterface::NMDeviceType newDevice) +{ + setTransportMedium(transportMediumFromDeviceType(newDevice)); } +void QNetworkManagerNetworkInformationBackend::onMeteredChanged( + QNetworkManagerInterface::NMMetered metered) +{ + setMetered(isMeteredFromNMMetered(metered)); +}; + QT_END_NAMESPACE #include "qnetworkmanagernetworkinformationbackend.moc" +#include "moc_qnetworkmanagernetworkinformationbackend.cpp" diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.h b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.h new file mode 100644 index 0000000000..3b60f0949c --- /dev/null +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.h @@ -0,0 +1,60 @@ +// 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 + +#ifndef QNETWORKMANAGERINFORMATIONBACKEND_H +#define QNETWORKMANAGERINFORMATIONBACKEND_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 <QtNetwork/private/qnetworkinformation_p.h> +#include "qnetworkmanagerservice.h" + +QT_BEGIN_NAMESPACE + +class QNetworkManagerNetworkInformationBackend : public QNetworkInformationBackend +{ + Q_OBJECT +public: + QNetworkManagerNetworkInformationBackend(); + ~QNetworkManagerNetworkInformationBackend() = default; + + QString name() const override; + 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 + | Feature::TransportMedium | Feature::Metered); + } + + bool isValid() const { return iface.isValid(); } + + void onStateChanged(QNetworkManagerInterface::NMState state); + void onConnectivityChanged(QNetworkManagerInterface::NMConnectivityState connectivityState); + void onDeviceTypeChanged(QNetworkManagerInterface::NMDeviceType deviceType); + void onMeteredChanged(QNetworkManagerInterface::NMMetered metered); + +private: + Q_DISABLE_COPY_MOVE(QNetworkManagerNetworkInformationBackend) + + QNetworkManagerInterface iface; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp index 764507fd4b..c055555cac 100644 --- a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp @@ -1,43 +1,8 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 #include "qnetworkmanagerservice.h" +#include "qnetworkmanagernetworkinformationbackend.h" #include <QObject> #include <QList> @@ -50,12 +15,40 @@ #include <QtDBus/QDBusObjectPath> #include <QtDBus/QDBusPendingCall> -#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" +#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"_L1 + +#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#define NM_DBUS_SERVICE NM_DBUS_INTERFACE ""_L1 + +#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"_L1 +#define NM_CONNECTION_DBUS_INTERFACE NM_DBUS_SERVICE ".Connection.Active"_L1 +#define NM_DEVICE_DBUS_INTERFACE NM_DBUS_SERVICE ".Device"_L1 QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +namespace { +constexpr QLatin1StringView propertiesChangedKey = "PropertiesChanged"_L1; +const QString &stateKey() +{ + static auto key = u"State"_s; + return key; +} +const QString &connectivityKey() +{ + static auto key = u"Connectivity"_s; + return key; +} +const QString &primaryConnectionKey() +{ + static auto key = u"PrimaryConnection"_s; + return key; +} +} + QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent) - : QDBusAbstractInterface(QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), + : QDBusAbstractInterface(NM_DBUS_SERVICE, NM_DBUS_PATH, NM_DBUS_INTERFACE, QDBusConnection::systemBus(), parent) { } @@ -68,75 +61,160 @@ bool QNetworkManagerInterfaceBase::networkManagerAvailable() QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent) : QNetworkManagerInterfaceBase(parent) { - if (!isValid()) + if (!QDBusAbstractInterface::isValid()) return; PropertiesDBusInterface managerPropertiesInterface( - QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), DBUS_PROPERTIES_INTERFACE, + NM_DBUS_SERVICE, NM_DBUS_PATH, DBUS_PROPERTIES_INTERFACE, QDBusConnection::systemBus()); QList<QVariant> argumentList; - argumentList << QLatin1String(NM_DBUS_INTERFACE); + argumentList << NM_DBUS_SERVICE; QDBusPendingReply<QVariantMap> propsReply = managerPropertiesInterface.callWithArgumentList( - QDBus::Block, QLatin1String("GetAll"), argumentList); - if (!propsReply.isError()) { - propertyMap = propsReply.value(); - } else { - qWarning() << "propsReply" << propsReply.error().message(); + QDBus::Block, "GetAll"_L1, argumentList); + if (propsReply.isError()) { + validDBusConnection = false; + if (auto error = propsReply.error(); error.type() != QDBusError::AccessDenied) + qWarning() << "Failed to query NetworkManager properties:" << error.message(); + return; } + propertyMap = propsReply.value(); - QDBusConnection::systemBus().connect( - QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH), - QLatin1String(NM_DBUS_INTERFACE), QLatin1String("PropertiesChanged"), this, - SLOT(setProperties(QMap<QString, QVariant>))); + validDBusConnection = QDBusConnection::systemBus().connect(NM_DBUS_SERVICE, NM_DBUS_PATH, + DBUS_PROPERTIES_INTERFACE, propertiesChangedKey, this, + SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); } 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>))); + QDBusConnection::systemBus().disconnect(NM_DBUS_SERVICE, NM_DBUS_PATH, + DBUS_PROPERTIES_INTERFACE, propertiesChangedKey, this, + SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>))); } QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const { - if (propertyMap.contains("State")) - return static_cast<QNetworkManagerInterface::NMState>(propertyMap.value("State").toUInt()); + auto it = propertyMap.constFind(stateKey()); + if (it != propertyMap.cend()) + return static_cast<QNetworkManagerInterface::NMState>(it->toUInt()); return QNetworkManagerInterface::NM_STATE_UNKNOWN; } QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connectivityState() const { - if (propertyMap.contains("Connectivity")) - return static_cast<NMConnectivityState>(propertyMap.value("Connectivity").toUInt()); + auto it = propertyMap.constFind(connectivityKey()); + if (it != propertyMap.cend()) + return static_cast<NMConnectivityState>(it->toUInt()); return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN; } -void QNetworkManagerInterface::setProperties(const QMap<QString, QVariant> &map) +static std::optional<QDBusInterface> getPrimaryDevice(const QDBusObjectPath &devicePath) +{ + const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(), + NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus()); + if (!connection.isValid()) + return std::nullopt; + + const auto devicePaths = connection.property("Devices").value<QList<QDBusObjectPath>>(); + if (devicePaths.isEmpty()) + return std::nullopt; + + const QDBusObjectPath primaryDevicePath = devicePaths.front(); + return std::make_optional<QDBusInterface>(NM_DBUS_SERVICE, primaryDevicePath.path(), + NM_DEVICE_DBUS_INTERFACE, + QDBusConnection::systemBus()); +} + +std::optional<QDBusObjectPath> QNetworkManagerInterface::primaryConnectionDevicePath() const +{ + auto it = propertyMap.constFind(primaryConnectionKey()); + if (it != propertyMap.cend()) + return it->value<QDBusObjectPath>(); + return std::nullopt; +} + +auto QNetworkManagerInterface::deviceType() const -> NMDeviceType { + if (const auto path = primaryConnectionDevicePath()) + return extractDeviceType(*path); + return NM_DEVICE_TYPE_UNKNOWN; +} + +auto QNetworkManagerInterface::meteredState() const -> NMMetered +{ + if (const auto path = primaryConnectionDevicePath()) + return extractDeviceMetered(*path); + return NM_METERED_UNKNOWN; +} + +auto QNetworkManagerInterface::extractDeviceType(const QDBusObjectPath &devicePath) const + -> NMDeviceType +{ + const auto primaryDevice = getPrimaryDevice(devicePath); + if (!primaryDevice) + return NM_DEVICE_TYPE_UNKNOWN; + if (!primaryDevice->isValid()) + return NM_DEVICE_TYPE_UNKNOWN; + const QVariant deviceType = primaryDevice->property("DeviceType"); + if (!deviceType.isValid()) + return NM_DEVICE_TYPE_UNKNOWN; + return static_cast<NMDeviceType>(deviceType.toUInt()); +} + +auto QNetworkManagerInterface::extractDeviceMetered(const QDBusObjectPath &devicePath) const + -> NMMetered +{ + const auto primaryDevice = getPrimaryDevice(devicePath); + if (!primaryDevice) + return NM_METERED_UNKNOWN; + if (!primaryDevice->isValid()) + return NM_METERED_UNKNOWN; + const QVariant metered = primaryDevice->property("Metered"); + if (!metered.isValid()) + return NM_METERED_UNKNOWN; + return static_cast<NMMetered>(metered.toUInt()); +} + +void QNetworkManagerInterface::setBackend(QNetworkManagerNetworkInformationBackend *ourBackend) +{ + backend = ourBackend; +} + +void QNetworkManagerInterface::setProperties(const QString &interfaceName, + const QMap<QString, QVariant> &map, + const QStringList &invalidatedProperties) +{ + Q_UNUSED(interfaceName); + Q_UNUSED(invalidatedProperties); + 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; + bool valueChanged = true; auto it = propertyMap.lowerBound(i.key()); if (it != propertyMap.end() && it.key() == i.key()) { - stateUpdate &= (it.value() != i.value()); - connectivityUpdate &= (it.value() != i.value()); + valueChanged = (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)); + if (valueChanged) { + if (i.key() == stateKey()) { + quint32 state = i.value().toUInt(); + backend->onStateChanged(static_cast<NMState>(state)); + } else if (i.key() == connectivityKey()) { + quint32 state = i.value().toUInt(); + backend->onConnectivityChanged(static_cast<NMConnectivityState>(state)); + } else if (i.key() == primaryConnectionKey()) { + const QDBusObjectPath devicePath = i->value<QDBusObjectPath>(); + backend->onDeviceTypeChanged(extractDeviceType(devicePath)); + backend->onMeteredChanged(extractDeviceMetered(devicePath)); + } else if (i.key() == "Metered"_L1) { + backend->onMeteredChanged(static_cast<NMMetered>(i->toUInt())); + } } } } QT_END_NAMESPACE + +#include "moc_qnetworkmanagerservice.cpp" diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h index 57c5aed763..5201e8485b 100644 --- a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h +++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// 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 #ifndef QNETWORKMANAGERSERVICE_H #define QNETWORKMANAGERSERVICE_H @@ -51,14 +15,11 @@ // We mean it. // -#include <QtDBus/QDBusAbstractInterface> -#include <QtDBus/QDBusPendingCallWatcher> -#include <QtDBus/QDBusObjectPath> +#include <QtCore/qvariant.h> +#include <QtCore/qmap.h> +#include <QtDBus/qdbusabstractinterface.h> -#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager" - -#define NM_DBUS_PATH "/org/freedesktop/NetworkManager" -#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager" +#include <optional> // Matches 'NMDeviceState' from https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html enum NMDeviceState { @@ -77,13 +38,16 @@ enum NMDeviceState { QT_BEGIN_NAMESPACE +class QDBusObjectPath; +class QNetworkManagerNetworkInformationBackend; + // 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); + explicit QNetworkManagerInterfaceBase(QObject *parent = nullptr); ~QNetworkManagerInterfaceBase() = default; static bool networkManagerAvailable(); @@ -119,31 +83,86 @@ public: NM_CONNECTIVITY_FULL = 4, }; Q_ENUM(NMConnectivityState); + // Matches 'NMDeviceType' from + // https://developer-old.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceType + enum NMDeviceType { + NM_DEVICE_TYPE_UNKNOWN = 0, + NM_DEVICE_TYPE_GENERIC = 14, + NM_DEVICE_TYPE_ETHERNET = 1, + NM_DEVICE_TYPE_WIFI = 2, + NM_DEVICE_TYPE_UNUSED1 = 3, + NM_DEVICE_TYPE_UNUSED2 = 4, + NM_DEVICE_TYPE_BT = 5, + NM_DEVICE_TYPE_OLPC_MESH = 6, + NM_DEVICE_TYPE_WIMAX = 7, + NM_DEVICE_TYPE_MODEM = 8, + NM_DEVICE_TYPE_INFINIBAND = 9, + NM_DEVICE_TYPE_BOND = 10, + NM_DEVICE_TYPE_VLAN = 11, + NM_DEVICE_TYPE_ADSL = 12, + NM_DEVICE_TYPE_BRIDGE = 13, + NM_DEVICE_TYPE_TEAM = 15, + NM_DEVICE_TYPE_TUN = 16, + NM_DEVICE_TYPE_IP_TUNNEL = 17, + NM_DEVICE_TYPE_MACVLAN = 18, + NM_DEVICE_TYPE_VXLAN = 19, + NM_DEVICE_TYPE_VETH = 20, + NM_DEVICE_TYPE_MACSEC = 21, + NM_DEVICE_TYPE_DUMMY = 22, + NM_DEVICE_TYPE_PPP = 23, + NM_DEVICE_TYPE_OVS_INTERFACE = 24, + NM_DEVICE_TYPE_OVS_PORT = 25, + NM_DEVICE_TYPE_OVS_BRIDGE = 26, + NM_DEVICE_TYPE_WPAN = 27, + NM_DEVICE_TYPE_6LOWPAN = 28, + NM_DEVICE_TYPE_WIREGUARD = 29, + NM_DEVICE_TYPE_WIFI_P2P = 30, + NM_DEVICE_TYPE_VRF = 31, + }; + // Matches 'NMMetered' from + // https://developer-old.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMMetered + enum NMMetered { + NM_METERED_UNKNOWN, + NM_METERED_YES, + NM_METERED_NO, + NM_METERED_GUESS_YES, + NM_METERED_GUESS_NO, + }; - QNetworkManagerInterface(QObject *parent = nullptr); + explicit QNetworkManagerInterface(QObject *parent = nullptr); ~QNetworkManagerInterface(); + void setBackend(QNetworkManagerNetworkInformationBackend *ourBackend); + NMState state() const; NMConnectivityState connectivityState() const; + NMDeviceType deviceType() const; + NMMetered meteredState() const; -Q_SIGNALS: - void stateChanged(NMState); - void connectivityChanged(NMConnectivityState); + bool isValid() const { return QDBusAbstractInterface::isValid() && validDBusConnection; } private Q_SLOTS: - void setProperties(const QMap<QString, QVariant> &map); + void setProperties(const QString &interfaceName, const QMap<QString, QVariant> &map, + const QStringList &invalidatedProperties); private: Q_DISABLE_COPY_MOVE(QNetworkManagerInterface) + NMDeviceType extractDeviceType(const QDBusObjectPath &devicePath) const; + NMMetered extractDeviceMetered(const QDBusObjectPath &devicePath) const; + + std::optional<QDBusObjectPath> primaryConnectionDevicePath() const; + QVariantMap propertyMap; + QNetworkManagerNetworkInformationBackend *backend = nullptr; + bool validDBusConnection = true; }; class PropertiesDBusInterface : public QDBusAbstractInterface { public: PropertiesDBusInterface(const QString &service, const QString &path, const QString &interface, - const QDBusConnection &connection, QObject *parent = 0) + const QDBusConnection &connection, QObject *parent = nullptr) : QDBusAbstractInterface(service, path, interface.toLatin1().data(), connection, parent) { } diff --git a/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt index 6a15bde1c0..a939ab4405 100644 --- a/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt +++ b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt @@ -1,7 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QSCNetworkReachabilityNetworkInformationPlugin - OUTPUT_NAME scnetworkreachability + OUTPUT_NAME qscnetworkreachability CLASS_NAME QSCNetworkReachabilityNetworkInformationBackendFactory - TYPE networkinformation + PLUGIN_TYPE networkinformation DEFAULT_IF APPLE SOURCES qscnetworkreachabilitynetworkinformationbackend.mm diff --git a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm index c732afc012..d1f3cb41d4 100644 --- a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm +++ b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm @@ -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 #include <QtNetwork/private/qnetworkinformation_p.h> @@ -48,8 +12,11 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR) Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.scnetworkreachability"); - -static QString backendName = QStringLiteral("scnetworkreachability"); +static QString backendName() +{ + return QString::fromUtf16(QNetworkInformationBackend::PluginNames + [QNetworkInformationBackend::PluginNamesAppleIndex]); +} class QSCNetworkReachabilityNetworkInformationBackend : public QNetworkInformationBackend { @@ -58,7 +25,7 @@ public: QSCNetworkReachabilityNetworkInformationBackend(); ~QSCNetworkReachabilityNetworkInformationBackend(); - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return featuresSupportedStatic(); @@ -66,12 +33,20 @@ public: static QNetworkInformation::Features featuresSupportedStatic() { - return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability); + return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability +#ifdef QT_PLATFORM_UIKIT + | QNetworkInformation::Feature::TransportMedium +#endif + ); } private Q_SLOTS: void reachabilityChanged(bool isOnline); +#ifdef QT_PLATFORM_UIKIT + void isWwanChanged(bool isOnline); +#endif + private: Q_DISABLE_COPY_MOVE(QSCNetworkReachabilityNetworkInformationBackend); @@ -87,7 +62,7 @@ class QSCNetworkReachabilityNetworkInformationBackendFactory : public QNetworkIn public: QSCNetworkReachabilityNetworkInformationBackendFactory() = default; ~QSCNetworkReachabilityNetworkInformationBackendFactory() = default; - QString name() const override { return backendName; } + QString name() const override { return backendName(); } QNetworkInformation::Features featuresSupported() const override { return QSCNetworkReachabilityNetworkInformationBackend::featuresSupportedStatic(); @@ -107,10 +82,16 @@ private: QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkInformationBackend() { bool isOnline = false; +#ifdef QT_PLATFORM_UIKIT + bool isWwan = false; +#endif if (ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) { // We manage to create SCNetworkReachabilityRef for IPv4, let's // read the last known state then! isOnline |= ipv4Probe.isReachable(); +#ifdef QT_PLATFORM_UIKIT + isWwan |= ipv4Probe.isWwan(); +#endif ipv4Probe.startMonitoring(); } @@ -118,9 +99,15 @@ QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkIn // We manage to create SCNetworkReachability ref for IPv6, let's // read the last known state then! isOnline |= ipv6Probe.isReachable(); +#ifdef QT_PLATFORM_UIKIT + isWwan |= ipv6Probe.isWwan(); +#endif ipv6Probe.startMonitoring(); } reachabilityChanged(isOnline); +#ifdef QT_PLATFORM_UIKIT + isWwanChanged(isWwan); +#endif connect(&ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, &QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged, @@ -128,6 +115,15 @@ QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkIn connect(&ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this, &QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged, Qt::QueuedConnection); + +#ifdef QT_PLATFORM_UIKIT + connect(&ipv4Probe, &QNetworkConnectionMonitor::isWwanChanged, this, + &QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged, + Qt::QueuedConnection); + connect(&ipv6Probe, &QNetworkConnectionMonitor::isWwanChanged, this, + &QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged, + Qt::QueuedConnection); +#endif } QSCNetworkReachabilityNetworkInformationBackend::~QSCNetworkReachabilityNetworkInformationBackend() @@ -140,6 +136,23 @@ void QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged(bool i : QNetworkInformation::Reachability::Disconnected); } +#ifdef QT_PLATFORM_UIKIT +void QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged(bool isWwan) +{ + // The reachability API from Apple only has one entry regarding transport medium: "IsWWAN"[0]. + // This is _serviceable_ on iOS where the only other credible options are "WLAN" or + // "Disconnected". But on macOS you could be connected by Ethernet as well, so how would that be + // reported? It doesn't matter anyway since "IsWWAN" is not available on macOS. + // [0]: https://developer.apple.com/documentation/systemconfiguration/scnetworkreachabilityflags/kscnetworkreachabilityflagsiswwan?language=objc + if (reachability() == QNetworkInformation::Reachability::Disconnected) { + setTransportMedium(QNetworkInformation::TransportMedium::Unknown); + } else { + setTransportMedium(isWwan ? QNetworkInformation::TransportMedium::Cellular + : QNetworkInformation::TransportMedium::WiFi); + } +} +#endif + QT_END_NAMESPACE #include "qscnetworkreachabilitynetworkinformationbackend.moc" |