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(FWEventKit EventKit)
|
||||||
qt_internal_find_apple_system_framework(FWHealthKit HealthKit)
|
qt_internal_find_apple_system_framework(FWHealthKit HealthKit)
|
||||||
qt_internal_find_apple_system_framework(FWUniformTypeIdentifiers UniformTypeIdentifiers)
|
qt_internal_find_apple_system_framework(FWUniformTypeIdentifiers UniformTypeIdentifiers)
|
||||||
|
qt_internal_find_apple_system_framework(FWNetwork Network)
|
||||||
endif()
|
endif()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
@ -255,7 +255,7 @@ qt_internal_extend_target(Network CONDITION APPLE
|
|||||||
SOURCES
|
SOURCES
|
||||||
kernel/qnetconmonitor_darwin.mm
|
kernel/qnetconmonitor_darwin.mm
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
${FWSystemConfiguration}
|
${FWNetwork}
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_internal_extend_target(Network CONDITION QT_FEATURE_networklistmanager AND NOT IOS AND NOT MACOS
|
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.
|
// 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
|
// 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/qnetconmonitor_p.h"
|
||||||
|
|
||||||
#include "private/qobject_p.h"
|
#include "private/qobject_p.h"
|
||||||
|
|
||||||
#include <SystemConfiguration/SystemConfiguration.h>
|
#include <Network/Network.h>
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <QtCore/qreadwritelock.h>
|
||||||
|
|
||||||
#include <QtCore/qmutex.h>
|
|
||||||
#include <QtCore/qwaitcondition.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -55,147 +48,140 @@ dispatch_queue_t qt_reachability_queue()
|
|||||||
return reachabilityQueue.data();
|
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
|
} // unnamed namespace
|
||||||
|
|
||||||
class QNetworkConnectionMonitorPrivate : public QObjectPrivate
|
class QNetworkConnectionMonitorPrivate : public QObjectPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SCNetworkReachabilityRef probe = nullptr;
|
nw_path_status_t status = nw_path_status_invalid;
|
||||||
SCNetworkReachabilityFlags state = kSCNetworkReachabilityFlagsIsLocalAddress;
|
mutable QReadWriteLock monitorLock;
|
||||||
bool scheduled = false;
|
nw_path_monitor_t monitor = nullptr;
|
||||||
|
using InterfaceType = QNetworkConnectionMonitor::InterfaceType;
|
||||||
|
InterfaceType interface = InterfaceType::Unknown;
|
||||||
|
|
||||||
QWaitCondition refCounterWaitCondition;
|
void updateState(nw_path_t newState);
|
||||||
QMutex refCounterMutex;
|
|
||||||
quint32 refCounter = 0;
|
|
||||||
|
|
||||||
void updateState(SCNetworkReachabilityFlags newState);
|
|
||||||
void reset();
|
void reset();
|
||||||
bool isReachable() const;
|
bool isReachable() const;
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
QNetworkConnectionMonitor::InterfaceType getInterfaceType() const;
|
||||||
bool isWwan() const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void retain()
|
bool startMonitoring();
|
||||||
{
|
void stopMonitoring();
|
||||||
QMutexLocker locker(&refCounterMutex);
|
bool isMonitoring() const;
|
||||||
++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);
|
|
||||||
|
|
||||||
Q_DECLARE_PUBLIC(QNetworkConnectionMonitor)
|
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.
|
// To be executed only on the reachability queue.
|
||||||
Q_Q(QNetworkConnectionMonitor);
|
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
|
// 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.
|
// setup. So in future this part and related can change/be extended.
|
||||||
const bool wasReachable = isReachable();
|
const bool wasReachable = isReachable();
|
||||||
|
const QNetworkConnectionMonitor::InterfaceType hadInterfaceType = interface;
|
||||||
|
const nw_path_status_t previousStatus = status;
|
||||||
|
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
status = nw_path_get_status(state);
|
||||||
const bool hadWwan = isWwan();
|
if (wasReachable != isReachable() || previousStatus == nw_path_status_invalid)
|
||||||
#endif
|
|
||||||
|
|
||||||
state = newState;
|
|
||||||
if (wasReachable != isReachable())
|
|
||||||
emit q->reachabilityChanged(isReachable());
|
emit q->reachabilityChanged(isReachable());
|
||||||
|
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
nw_path_enumerate_interfaces(state, ^(nw_interface_t nwInterface) {
|
||||||
if (hadWwan != isWwan())
|
if (nw_path_uses_interface_type(state, nw_interface_get_type(nwInterface))) {
|
||||||
emit q->isWwanChanged(isWwan());
|
const nw_interface_type_t type = nw_interface_get_type(nwInterface);
|
||||||
#endif
|
|
||||||
|
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()
|
void QNetworkConnectionMonitorPrivate::reset()
|
||||||
{
|
{
|
||||||
if (probe) {
|
stopMonitoring();
|
||||||
CFRelease(probe);
|
status = nw_path_status_invalid;
|
||||||
probe = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = kSCNetworkReachabilityFlagsIsLocalAddress;
|
|
||||||
scheduled = false;
|
|
||||||
waitForRefCountZero();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QNetworkConnectionMonitorPrivate::isReachable() const
|
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
|
QNetworkConnectionMonitor::InterfaceType QNetworkConnectionMonitorPrivate::getInterfaceType() const
|
||||||
bool QNetworkConnectionMonitorPrivate::isWwan() const
|
|
||||||
{
|
{
|
||||||
return !!(state & kSCNetworkReachabilityFlagsIsWWAN);
|
return interface;
|
||||||
}
|
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *QNetworkConnectionMonitorPrivate::retainInfo(const void *info)
|
bool QNetworkConnectionMonitorPrivate::startMonitoring()
|
||||||
{
|
{
|
||||||
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
|
QWriteLocker lock(&monitorLock);
|
||||||
monitorPrivate->retain();
|
monitor = nw_path_monitor_create();
|
||||||
return info;
|
if (monitor == nullptr) {
|
||||||
|
qCWarning(lcNetMon, "Failed to create a path monitor, cannot determine current reachability.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QNetworkConnectionMonitorPrivate::releaseInfo(const void *info)
|
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::stopMonitoring()
|
||||||
{
|
{
|
||||||
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
|
QWriteLocker lock(&monitorLock);
|
||||||
monitorPrivate->release();
|
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()
|
QNetworkConnectionMonitor::QNetworkConnectionMonitor()
|
||||||
@ -203,115 +189,32 @@ QNetworkConnectionMonitor::QNetworkConnectionMonitor()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote)
|
QNetworkConnectionMonitor::QNetworkConnectionMonitor(const QHostAddress &/*local*/, const QHostAddress &/*remote*/)
|
||||||
: QObject(*new QNetworkConnectionMonitorPrivate)
|
: QNetworkConnectionMonitor()
|
||||||
{
|
{
|
||||||
setTargets(local, remote);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkConnectionMonitor::~QNetworkConnectionMonitor()
|
QNetworkConnectionMonitor::~QNetworkConnectionMonitor()
|
||||||
{
|
{
|
||||||
Q_D(QNetworkConnectionMonitor);
|
Q_D(QNetworkConnectionMonitor);
|
||||||
|
|
||||||
stopMonitoring();
|
|
||||||
d->reset();
|
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;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QNetworkConnectionMonitor::startMonitoring()
|
bool QNetworkConnectionMonitor::startMonitoring()
|
||||||
{
|
{
|
||||||
Q_D(QNetworkConnectionMonitor);
|
Q_D(QNetworkConnectionMonitor);
|
||||||
|
|
||||||
if (isMonitoring()) {
|
if (d->isMonitoring()) {
|
||||||
qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
|
qCWarning(lcNetMon, "Monitor is already active, call stopMonitoring() first");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d->probe) {
|
return d->startMonitoring();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QNetworkConnectionMonitor::isReachable()
|
bool QNetworkConnectionMonitor::isReachable()
|
||||||
@ -323,36 +226,30 @@ bool QNetworkConnectionMonitor::isReachable()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d->probe) {
|
|
||||||
qCWarning(lcNetMon, "Reachability is unknown, set the target first");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return d->isReachable();
|
return d->isReachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
QNetworkConnectionMonitor::InterfaceType QNetworkConnectionMonitor::getInterfaceType() const
|
||||||
bool QNetworkConnectionMonitor::isWwan() const
|
|
||||||
{
|
{
|
||||||
Q_D(const QNetworkConnectionMonitor);
|
Q_D(const QNetworkConnectionMonitor);
|
||||||
|
|
||||||
if (isMonitoring()) {
|
if (d->isMonitoring()) {
|
||||||
qCWarning(lcNetMon, "Calling isWwan() is unsafe after the monitoring started");
|
qCWarning(lcNetMon, "Calling getInterfaceType() is unsafe after the monitoring started");
|
||||||
return false;
|
return QNetworkConnectionMonitor::InterfaceType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d->probe) {
|
return d->getInterfaceType();
|
||||||
qCWarning(lcNetMon, "Medium is unknown, set the target first");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return d->isWwan();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool QNetworkConnectionMonitor::isEnabled()
|
bool QNetworkConnectionMonitor::isEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QNetworkConnectionMonitor::isMonitoring() const
|
||||||
|
{
|
||||||
|
Q_D(const QNetworkConnectionMonitor);
|
||||||
|
return d->isMonitoring();
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -29,15 +29,24 @@ class Q_NETWORK_EXPORT QNetworkConnectionMonitor : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifdef Q_OS_APPLE
|
||||||
|
enum class InterfaceType {
|
||||||
|
Unknown,
|
||||||
|
Ethernet,
|
||||||
|
Cellular,
|
||||||
|
WiFi,
|
||||||
|
};
|
||||||
|
Q_ENUM(InterfaceType)
|
||||||
|
#endif
|
||||||
|
|
||||||
QNetworkConnectionMonitor();
|
QNetworkConnectionMonitor();
|
||||||
QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote = {});
|
QNetworkConnectionMonitor(const QHostAddress &local, const QHostAddress &remote = {});
|
||||||
~QNetworkConnectionMonitor();
|
~QNetworkConnectionMonitor();
|
||||||
|
|
||||||
bool setTargets(const QHostAddress &local, const QHostAddress &remote);
|
bool setTargets(const QHostAddress &local, const QHostAddress &remote);
|
||||||
bool isReachable();
|
bool isReachable();
|
||||||
|
#ifdef Q_OS_APPLE
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
InterfaceType getInterfaceType() const;
|
||||||
bool isWwan() const;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Important: on Darwin you should not call isReachable/isWwan() after
|
// Important: on Darwin you should not call isReachable/isWwan() after
|
||||||
@ -53,9 +62,8 @@ Q_SIGNALS:
|
|||||||
// Important: connect to this using QueuedConnection. On Darwin
|
// Important: connect to this using QueuedConnection. On Darwin
|
||||||
// callback is coming on a special dispatch queue.
|
// callback is coming on a special dispatch queue.
|
||||||
void reachabilityChanged(bool isOnline);
|
void reachabilityChanged(bool isOnline);
|
||||||
|
#ifdef Q_OS_APPLE
|
||||||
#ifdef QT_PLATFORM_UIKIT
|
void interfaceTypeChanged(InterfaceType type);
|
||||||
void isWwanChanged(bool isWwan);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -646,7 +646,7 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
|
|||||||
\li networklistmanager
|
\li networklistmanager
|
||||||
\row
|
\row
|
||||||
\li Apple (macOS/iOS)
|
\li Apple (macOS/iOS)
|
||||||
\li scnetworkreachability
|
\li applenetworkinformation
|
||||||
\row
|
\row
|
||||||
\li Android
|
\li Android
|
||||||
\li android
|
\li android
|
||||||
|
@ -32,9 +32,9 @@ class Q_NETWORK_EXPORT QNetworkInformationBackend : public QObject
|
|||||||
using TransportMedium = QNetworkInformation::TransportMedium;
|
using TransportMedium = QNetworkInformation::TransportMedium;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static inline const char16_t PluginNames[4][22] = {
|
static inline const char16_t PluginNames[4][24] = {
|
||||||
{ u"networklistmanager" },
|
{ u"networklistmanager" },
|
||||||
{ u"scnetworkreachability" },
|
{ u"applenetworkinformation" },
|
||||||
{ u"android" },
|
{ u"android" },
|
||||||
{ u"networkmanager" },
|
{ u"networkmanager" },
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@ if(LINUX AND TARGET Qt::DBus)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
add_subdirectory(scnetworkreachability)
|
add_subdirectory(apple)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
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