QNCM[Mac]: Update from SCNetworkReachability to Network framework

Since SCNetworkReachability is deprecated,
the implementation is now updated to use the Network framework.

[ChangeLog][QNetworkInformation] Replace deprecated
SCNetworkReachability implementation with Network framework (iOS, macOS)

Fixes: QTBUG-132103
Change-Id: Iff4a667fd07002451b7d49cc8fd069945d426aab
Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Vladimir Belyavsky <belyavskyv@gmail.com>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io>
This commit is contained in:
Evgen Pervenenko 2024-11-23 11:02:34 +03:00
parent 311ac55f11
commit 1299aaa231
11 changed files with 270 additions and 399 deletions

View File

@ -38,6 +38,7 @@ macro(qt_find_apple_system_frameworks)
qt_internal_find_apple_system_framework(FWEventKit EventKit)
qt_internal_find_apple_system_framework(FWHealthKit HealthKit)
qt_internal_find_apple_system_framework(FWUniformTypeIdentifiers UniformTypeIdentifiers)
qt_internal_find_apple_system_framework(FWNetwork Network)
endif()
endmacro()

View File

@ -255,7 +255,7 @@ qt_internal_extend_target(Network CONDITION APPLE
SOURCES
kernel/qnetconmonitor_darwin.mm
LIBRARIES
${FWSystemConfiguration}
${FWNetwork}
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_networklistmanager AND NOT IOS AND NOT MACOS

View File

@ -1,20 +1,13 @@
// Copyright (C) 2019 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 "private/qnativesocketengine_p_p.h"
#include "private/qnetconmonitor_p.h"
#include "private/qobject_p.h"
#include <SystemConfiguration/SystemConfiguration.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Network/Network.h>
#include <netinet/in.h>
#include <QtCore/qmutex.h>
#include <QtCore/qwaitcondition.h>
#include <cstring>
#include <QtCore/qreadwritelock.h>
QT_BEGIN_NAMESPACE
@ -55,147 +48,140 @@ dispatch_queue_t qt_reachability_queue()
return reachabilityQueue.data();
}
qt_sockaddr qt_hostaddress_to_sockaddr(const QHostAddress &src)
{
if (src.isNull())
return {};
qt_sockaddr dst;
if (src.protocol() == QAbstractSocket::IPv4Protocol) {
dst.a4 = sockaddr_in{};
dst.a4.sin_family = AF_INET;
dst.a4.sin_addr.s_addr = htonl(src.toIPv4Address());
dst.a4.sin_len = sizeof(sockaddr_in);
} else if (src.protocol() == QAbstractSocket::IPv6Protocol) {
dst.a6 = sockaddr_in6{};
dst.a6.sin6_family = AF_INET6;
dst.a6.sin6_len = sizeof(sockaddr_in6);
const Q_IPV6ADDR ipv6 = src.toIPv6Address();
std::memcpy(&dst.a6.sin6_addr, &ipv6, sizeof ipv6);
} else {
Q_UNREACHABLE();
}
return dst;
}
} // unnamed namespace
class QNetworkConnectionMonitorPrivate : public QObjectPrivate
{
public:
SCNetworkReachabilityRef probe = nullptr;
SCNetworkReachabilityFlags state = kSCNetworkReachabilityFlagsIsLocalAddress;
bool scheduled = false;
nw_path_status_t status = nw_path_status_invalid;
mutable QReadWriteLock monitorLock;
nw_path_monitor_t monitor = nullptr;
using InterfaceType = QNetworkConnectionMonitor::InterfaceType;
InterfaceType interface = InterfaceType::Unknown;
QWaitCondition refCounterWaitCondition;
QMutex refCounterMutex;
quint32 refCounter = 0;
void updateState(SCNetworkReachabilityFlags newState);
void updateState(nw_path_t newState);
void reset();
bool isReachable() const;
#ifdef QT_PLATFORM_UIKIT
bool isWwan() const;
#endif
QNetworkConnectionMonitor::InterfaceType getInterfaceType() const;
void retain()
{
QMutexLocker locker(&refCounterMutex);
++refCounter;
}
void release()
{
QMutexLocker locker(&refCounterMutex);
if (--refCounter == 0)
refCounterWaitCondition.wakeAll();
}
void waitForRefCountZero()
{
QMutexLocker locker(&refCounterMutex);
while (refCounter > 0) {
refCounterWaitCondition.wait(&refCounterMutex);
}
}
static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info);
static const void *retainInfo(const void* info);
static void releaseInfo(const void* info);
bool startMonitoring();
void stopMonitoring();
bool isMonitoring() const;
Q_DECLARE_PUBLIC(QNetworkConnectionMonitor)
};
void QNetworkConnectionMonitorPrivate::updateState(SCNetworkReachabilityFlags newState)
void QNetworkConnectionMonitorPrivate::updateState(nw_path_t state)
{
QReadLocker lock(&monitorLock);
if (monitor == nullptr)
return;
// To be executed only on the reachability queue.
Q_Q(QNetworkConnectionMonitor);
// NETMONTODO: for now, 'online' for us means kSCNetworkReachabilityFlagsReachable
// NETMONTODO: for now, 'online' for us means nw_path_status_satisfied
// is set. There are more possible flags that require more tests/some special
// setup. So in future this part and related can change/be extended.
const bool wasReachable = isReachable();
const QNetworkConnectionMonitor::InterfaceType hadInterfaceType = interface;
const nw_path_status_t previousStatus = status;
#ifdef QT_PLATFORM_UIKIT
const bool hadWwan = isWwan();
#endif
state = newState;
if (wasReachable != isReachable())
status = nw_path_get_status(state);
if (wasReachable != isReachable() || previousStatus == nw_path_status_invalid)
emit q->reachabilityChanged(isReachable());
#ifdef QT_PLATFORM_UIKIT
if (hadWwan != isWwan())
emit q->isWwanChanged(isWwan());
#endif
nw_path_enumerate_interfaces(state, ^(nw_interface_t nwInterface) {
if (nw_path_uses_interface_type(state, nw_interface_get_type(nwInterface))) {
const nw_interface_type_t type = nw_interface_get_type(nwInterface);
switch (type) {
case nw_interface_type_wifi:
interface = QNetworkConnectionMonitor::InterfaceType::WiFi;
break;
case nw_interface_type_cellular:
interface = QNetworkConnectionMonitor::InterfaceType::Cellular;
break;
case nw_interface_type_wired:
interface = QNetworkConnectionMonitor::InterfaceType::Ethernet;
break;
default:
interface = QNetworkConnectionMonitor::InterfaceType::Unknown;
break;
}
return false;
}
return true;
});
if (hadInterfaceType != interface)
emit q->interfaceTypeChanged(interface);
}
void QNetworkConnectionMonitorPrivate::reset()
{
if (probe) {
CFRelease(probe);
probe = nullptr;
}
state = kSCNetworkReachabilityFlagsIsLocalAddress;
scheduled = false;
waitForRefCountZero();
stopMonitoring();
status = nw_path_status_invalid;
}
bool QNetworkConnectionMonitorPrivate::isReachable() const
{
return !!(state & kSCNetworkReachabilityFlagsReachable);
return status == nw_path_status_satisfied;
}
#ifdef QT_PLATFORM_UIKIT // The IsWWAN flag is not available on macOS
bool QNetworkConnectionMonitorPrivate::isWwan() const
QNetworkConnectionMonitor::InterfaceType QNetworkConnectionMonitorPrivate::getInterfaceType() const
{
return !!(state & kSCNetworkReachabilityFlagsIsWWAN);
}
#endif
void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info)
{
// To be executed only on the reachability queue.
Q_UNUSED(probe);
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate *>(info);
Q_ASSERT(monitorPrivate);
monitorPrivate->updateState(flags);
return interface;
}
const void *QNetworkConnectionMonitorPrivate::retainInfo(const void *info)
bool QNetworkConnectionMonitorPrivate::startMonitoring()
{
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
monitorPrivate->retain();
return info;
QWriteLocker lock(&monitorLock);
monitor = nw_path_monitor_create();
if (monitor == nullptr) {
qCWarning(lcNetMon, "Failed to create a path monitor, cannot determine current reachability.");
return false;
}
nw_path_monitor_set_update_handler(monitor, [this](nw_path_t path){
updateState(path);
});
auto queue = qt_reachability_queue();
if (!queue) {
qCWarning(lcNetMon, "Failed to create a dispatch queue to schedule a probe on");
nw_release(monitor);
monitor = nullptr;
return false;
}
nw_path_monitor_set_queue(monitor, queue);
nw_path_monitor_start(monitor);
return true;
}
void QNetworkConnectionMonitorPrivate::releaseInfo(const void *info)
void QNetworkConnectionMonitorPrivate::stopMonitoring()
{
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
monitorPrivate->release();
QWriteLocker lock(&monitorLock);
if (monitor != nullptr) {
nw_path_monitor_cancel(monitor);
nw_release(monitor);
monitor = nullptr;
}
}
void QNetworkConnectionMonitor::stopMonitoring()
{
Q_D(QNetworkConnectionMonitor);
d->stopMonitoring();
}
bool QNetworkConnectionMonitorPrivate::isMonitoring() const
{
QReadLocker lock(&monitorLock);
return monitor != nullptr;
}
QNetworkConnectionMonitor::QNetworkConnectionMonitor()
@ -203,115 +189,32 @@ QNetworkConnectionMonitor::QNetworkConnectionMonitor()
{
}
QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote)
: QObject(*new QNetworkConnectionMonitorPrivate)
QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &/*local*/, const QHostAddress &/*remote*/)
: QNetworkConnectionMonitor()
{
setTargets(local, remote);
}
QNetworkConnectionMonitor::~QNetworkConnectionMonitor()
{
Q_D(QNetworkConnectionMonitor);
stopMonitoring();
d->reset();
}
bool QNetworkConnectionMonitor::setTargets(const QHostAddress &local, const QHostAddress &remote)
bool QNetworkConnectionMonitor::setTargets(const QHostAddress &/*local*/, const QHostAddress &/*remote*/)
{
Q_D(QNetworkConnectionMonitor);
if (isMonitoring()) {
qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
return false;
}
if (local.isNull()) {
qCWarning(lcNetMon, "Invalid (null) local address, cannot create a reachability target");
return false;
}
// Clear the old target if needed:
d->reset();
qt_sockaddr client = qt_hostaddress_to_sockaddr(local);
if (remote.isNull()) {
// That's a special case our QNetworkInformation backend is using (AnyIpv4/6 address to check an overall status).
d->probe = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, reinterpret_cast<sockaddr *>(&client));
} else {
qt_sockaddr target = qt_hostaddress_to_sockaddr(remote);
d->probe = SCNetworkReachabilityCreateWithAddressPair(kCFAllocatorDefault,
reinterpret_cast<sockaddr *>(&client),
reinterpret_cast<sockaddr *>(&target));
}
if (d->probe) {
// Let's read the initial state so that callback coming later can
// see a difference. Ignore errors though.
SCNetworkReachabilityGetFlags(d->probe, &d->state);
}else {
qCWarning(lcNetMon, "Failed to create network reachability probe");
return false;
}
return true;
return false;
}
bool QNetworkConnectionMonitor::startMonitoring()
{
Q_D(QNetworkConnectionMonitor);
if (isMonitoring()) {
if (d->isMonitoring()) {
qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
return false;
}
if (!d->probe) {
qCWarning(lcNetMon, "Can not start monitoring, set targets first");
return false;
}
auto queue = qt_reachability_queue();
if (!queue) {
qCWarning(lcNetMon, "Failed to create a dispatch queue to schedule a probe on");
return false;
}
SCNetworkReachabilityContext context = {};
context.info = d;
context.retain = QNetworkConnectionMonitorPrivate::retainInfo;
context.release = QNetworkConnectionMonitorPrivate::releaseInfo;
if (!SCNetworkReachabilitySetCallback(d->probe, QNetworkConnectionMonitorPrivate::probeCallback, &context)) {
qCWarning(lcNetMon, "Failed to set a reachability callback");
return false;
}
if (!SCNetworkReachabilitySetDispatchQueue(d->probe, queue)) {
qCWarning(lcNetMon, "Failed to schedule a reachability callback on a queue");
return false;
}
return d->scheduled = true;
}
bool QNetworkConnectionMonitor::isMonitoring() const
{
Q_D(const QNetworkConnectionMonitor);
return d->scheduled;
}
void QNetworkConnectionMonitor::stopMonitoring()
{
Q_D(QNetworkConnectionMonitor);
if (d->scheduled) {
Q_ASSERT(d->probe);
SCNetworkReachabilitySetDispatchQueue(d->probe, nullptr);
SCNetworkReachabilitySetCallback(d->probe, nullptr, nullptr);
d->scheduled = false;
}
return d->startMonitoring();
}
bool QNetworkConnectionMonitor::isReachable()
@ -323,36 +226,30 @@ bool QNetworkConnectionMonitor::isReachable()
return false;
}
if (!d->probe) {
qCWarning(lcNetMon, "Reachability is unknown, set the target first");
return false;
}
return d->isReachable();
}
#ifdef QT_PLATFORM_UIKIT
bool QNetworkConnectionMonitor::isWwan() const
QNetworkConnectionMonitor::InterfaceType QNetworkConnectionMonitor::getInterfaceType() const
{
Q_D(const QNetworkConnectionMonitor);
if (isMonitoring()) {
qCWarning(lcNetMon, "Calling isWwan() is unsafe after the monitoring started");
return false;
if (d->isMonitoring()) {
qCWarning(lcNetMon, "Calling getInterfaceType() is unsafe after the monitoring started");
return QNetworkConnectionMonitor::InterfaceType::Unknown;
}
if (!d->probe) {
qCWarning(lcNetMon, "Medium is unknown, set the target first");
return false;
}
return d->isWwan();
return d->getInterfaceType();
}
#endif
bool QNetworkConnectionMonitor::isEnabled()
{
return true;
}
bool QNetworkConnectionMonitor::isMonitoring() const
{
Q_D(const QNetworkConnectionMonitor);
return d->isMonitoring();
}
QT_END_NAMESPACE

View File

@ -29,15 +29,24 @@ class Q_NETWORK_EXPORT QNetworkConnectionMonitor : public QObject
Q_OBJECT
public:
#ifdef Q_OS_APPLE
enum class InterfaceType {
Unknown,
Ethernet,
Cellular,
WiFi,
};
Q_ENUM(InterfaceType)
#endif
QNetworkConnectionMonitor();
QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote = {});
~QNetworkConnectionMonitor();
bool setTargets(const QHostAddress &local, const QHostAddress &remote);
bool isReachable();
#ifdef QT_PLATFORM_UIKIT
bool isWwan() const;
#ifdef Q_OS_APPLE
InterfaceType getInterfaceType() const;
#endif
// Important: on Darwin you should not call isReachable/isWwan() after
@ -53,9 +62,8 @@ Q_SIGNALS:
// Important: connect to this using QueuedConnection. On Darwin
// callback is coming on a special dispatch queue.
void reachabilityChanged(bool isOnline);
#ifdef QT_PLATFORM_UIKIT
void isWwanChanged(bool isWwan);
#ifdef Q_OS_APPLE
void interfaceTypeChanged(InterfaceType type);
#endif
private:

View File

@ -646,7 +646,7 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
\li networklistmanager
\row
\li Apple (macOS/iOS)
\li scnetworkreachability
\li applenetworkinformation
\row
\li Android
\li android

View File

@ -32,9 +32,9 @@ class Q_NETWORK_EXPORT QNetworkInformationBackend : public QObject
using TransportMedium = QNetworkInformation::TransportMedium;
public:
static inline const char16_t PluginNames[4][22] = {
static inline const char16_t PluginNames[4][24] = {
{ u"networklistmanager" },
{ u"scnetworkreachability" },
{ u"applenetworkinformation" },
{ u"android" },
{ u"networkmanager" },
};

View File

@ -10,7 +10,7 @@ if(LINUX AND TARGET Qt::DBus)
endif()
if(APPLE)
add_subdirectory(scnetworkreachability)
add_subdirectory(apple)
endif()
if(ANDROID)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_plugin(QAppleNetworkInformationPlugin
OUTPUT_NAME qapplenetworkinformation
CLASS_NAME QAppleNetworkInformationBackendFactory
PLUGIN_TYPE networkinformation
DEFAULT_IF APPLE
SOURCES
qapplenetworkinformationbackend.mm
LIBRARIES
Qt::NetworkPrivate
${FWNetwork}
)

View File

@ -0,0 +1,123 @@
// Copyright (C) 2024 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 <QtNetwork/private/qnetconmonitor_p.h>
#include <QtCore/qglobal.h>
#include <QtCore/private/qobject_p.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR)
Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.applenetworkinfo");
static QString backendName()
{
return QString::fromUtf16(QNetworkInformationBackend::PluginNames
[QNetworkInformationBackend::PluginNamesAppleIndex]);
}
class QAppleNetworkInformationBackend : public QNetworkInformationBackend
{
Q_OBJECT
public:
QAppleNetworkInformationBackend();
~QAppleNetworkInformationBackend();
QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return featuresSupportedStatic();
}
static QNetworkInformation::Features featuresSupportedStatic()
{
return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability
| QNetworkInformation::Feature::TransportMedium);
}
private Q_SLOTS:
void reachabilityChanged(bool isOnline);
void interfaceTypeChanged(QNetworkConnectionMonitor::InterfaceType type);
private:
Q_DISABLE_COPY_MOVE(QAppleNetworkInformationBackend)
QNetworkConnectionMonitor probe;
};
class QAppleNetworkInformationBackendFactory : public QNetworkInformationBackendFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid)
Q_INTERFACES(QNetworkInformationBackendFactory)
public:
QAppleNetworkInformationBackendFactory() = default;
~QAppleNetworkInformationBackendFactory() = default;
QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return QAppleNetworkInformationBackend::featuresSupportedStatic();
}
QNetworkInformationBackend *create(
QNetworkInformation::Features requiredFeatures) const override
{
if ((requiredFeatures & featuresSupported()) != requiredFeatures)
return nullptr;
return new QAppleNetworkInformationBackend();
}
private:
Q_DISABLE_COPY_MOVE(QAppleNetworkInformationBackendFactory)
};
QAppleNetworkInformationBackend::QAppleNetworkInformationBackend()
{
connect(&probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
&QAppleNetworkInformationBackend::reachabilityChanged,
Qt::QueuedConnection);
connect(&probe, &QNetworkConnectionMonitor::interfaceTypeChanged, this,
&QAppleNetworkInformationBackend::interfaceTypeChanged,
Qt::QueuedConnection);
probe.startMonitoring();
}
QAppleNetworkInformationBackend::~QAppleNetworkInformationBackend()
{
}
void QAppleNetworkInformationBackend::reachabilityChanged(bool isOnline)
{
setReachability(isOnline ? QNetworkInformation::Reachability::Online
: QNetworkInformation::Reachability::Disconnected);
}
void QAppleNetworkInformationBackend::interfaceTypeChanged(
QNetworkConnectionMonitor::InterfaceType type)
{
if (reachability() == QNetworkInformation::Reachability::Disconnected) {
setTransportMedium(QNetworkInformation::TransportMedium::Unknown);
} else {
switch (type) {
case QNetworkConnectionMonitor::InterfaceType::Ethernet:
setTransportMedium(QNetworkInformation::TransportMedium::Ethernet);
break;
case QNetworkConnectionMonitor::InterfaceType::Cellular:
setTransportMedium(QNetworkInformation::TransportMedium::Cellular);
break;
case QNetworkConnectionMonitor::InterfaceType::WiFi:
setTransportMedium(QNetworkInformation::TransportMedium::WiFi);
break;
case QNetworkConnectionMonitor::InterfaceType::Unknown:
setTransportMedium(QNetworkInformation::TransportMedium::Unknown);
break;
}
}
}
QT_END_NAMESPACE
#include "qapplenetworkinformationbackend.moc"

View File

@ -1,14 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_plugin(QSCNetworkReachabilityNetworkInformationPlugin
OUTPUT_NAME qscnetworkreachability
CLASS_NAME QSCNetworkReachabilityNetworkInformationBackendFactory
PLUGIN_TYPE networkinformation
DEFAULT_IF APPLE
SOURCES
qscnetworkreachabilitynetworkinformationbackend.mm
LIBRARIES
Qt::NetworkPrivate
${FWSystemConfiguration}
)

View File

@ -1,158 +0,0 @@
// 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 <QtNetwork/private/qnetconmonitor_p.h>
#include <QtCore/qglobal.h>
#include <QtCore/private/qobject_p.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR)
Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.scnetworkreachability");
static QString backendName()
{
return QString::fromUtf16(QNetworkInformationBackend::PluginNames
[QNetworkInformationBackend::PluginNamesAppleIndex]);
}
class QSCNetworkReachabilityNetworkInformationBackend : public QNetworkInformationBackend
{
Q_OBJECT
public:
QSCNetworkReachabilityNetworkInformationBackend();
~QSCNetworkReachabilityNetworkInformationBackend();
QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return featuresSupportedStatic();
}
static QNetworkInformation::Features featuresSupportedStatic()
{
return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability
#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);
QNetworkConnectionMonitor ipv4Probe;
QNetworkConnectionMonitor ipv6Probe;
};
class QSCNetworkReachabilityNetworkInformationBackendFactory : public QNetworkInformationBackendFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid)
Q_INTERFACES(QNetworkInformationBackendFactory)
public:
QSCNetworkReachabilityNetworkInformationBackendFactory() = default;
~QSCNetworkReachabilityNetworkInformationBackendFactory() = default;
QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return QSCNetworkReachabilityNetworkInformationBackend::featuresSupportedStatic();
}
QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const override
{
if ((requiredFeatures & featuresSupported()) != requiredFeatures)
return nullptr;
return new QSCNetworkReachabilityNetworkInformationBackend();
}
private:
Q_DISABLE_COPY_MOVE(QSCNetworkReachabilityNetworkInformationBackendFactory);
};
QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkInformationBackend()
{
bool isOnline = false;
#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();
}
if (ipv6Probe.setTargets(QHostAddress::AnyIPv6, {})) {
// 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,
Qt::QueuedConnection);
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()
{
}
void QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged(bool isOnline)
{
setReachability(isOnline ? QNetworkInformation::Reachability::Online
: 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"