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:
parent
311ac55f11
commit
1299aaa231
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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" },
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ if(LINUX AND TARGET Qt::DBus)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(scnetworkreachability)
|
||||
add_subdirectory(apple)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
|
14
src/plugins/networkinformation/apple/CMakeLists.txt
Normal file
14
src/plugins/networkinformation/apple/CMakeLists.txt
Normal 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}
|
||||
)
|
@ -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"
|
@ -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}
|
||||
)
|
@ -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"
|
Loading…
x
Reference in New Issue
Block a user