From a6947853ee0181098d963dab315c59b926187c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Mon, 18 Oct 2021 17:48:36 +0200 Subject: [PATCH] QNI: transportMedium support for iOS It can only differentiate between cellular and not cellular and then we can determine if it's disconnected or (presumably) using wifi. It is also explicitly not supported on macOS, which adds to the confusion. Task-number: QTBUG-91023 Change-Id: I1d002ba06dd9acf1a0daabfb2a4193c07871e9b4 Reviewed-by: Timur Pocheptsov Reviewed-by: Edward Welbourne Reviewed-by: Qt CI Bot --- src/network/kernel/qnetconmonitor_darwin.mm | 39 +++++++++++++++ src/network/kernel/qnetconmonitor_p.h | 10 +++- ...rkreachabilitynetworkinformationbackend.mm | 48 ++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/network/kernel/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm index 42540c38ee1..a899d3b98d9 100644 --- a/src/network/kernel/qnetconmonitor_darwin.mm +++ b/src/network/kernel/qnetconmonitor_darwin.mm @@ -124,6 +124,9 @@ public: void updateState(SCNetworkReachabilityFlags newState); void reset(); bool isReachable() const; +#ifdef QT_PLATFORM_UIKIT + bool isWwan() const; +#endif static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info); @@ -139,9 +142,19 @@ void QNetworkConnectionMonitorPrivate::updateState(SCNetworkReachabilityFlags ne // 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(); + +#ifdef QT_PLATFORM_UIKIT + const bool hadWwan = isWwan(); +#endif + state = newState; if (wasReachable != isReachable()) emit q->reachabilityChanged(isReachable()); + +#ifdef QT_PLATFORM_UIKIT + if (hadWwan != isWwan()) + emit q->isWwanChanged(isWwan()); +#endif } void QNetworkConnectionMonitorPrivate::reset() @@ -160,6 +173,13 @@ bool QNetworkConnectionMonitorPrivate::isReachable() const return !!(state & kSCNetworkReachabilityFlagsReachable); } +#ifdef QT_PLATFORM_UIKIT // The IsWWAN flag is not available on macOS +bool QNetworkConnectionMonitorPrivate::isWwan() const +{ + return !!(state & kSCNetworkReachabilityFlagsIsWWAN); +} +#endif + void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info) { // To be executed only on the reachability queue. @@ -301,6 +321,25 @@ bool QNetworkConnectionMonitor::isReachable() return d->isReachable(); } +#ifdef QT_PLATFORM_UIKIT +bool QNetworkConnectionMonitor::isWwan() const +{ + Q_D(const QNetworkConnectionMonitor); + + if (isMonitoring()) { + qCWarning(lcNetMon, "Calling isReachable() is unsafe after the monitoring started"); + return false; + } + + if (!d->probe) { + qCWarning(lcNetMon, "Reachability is unknown, set the target first"); + return false; + } + + return d->isWwan(); +} +#endif + bool QNetworkConnectionMonitor::isEnabled() { return true; diff --git a/src/network/kernel/qnetconmonitor_p.h b/src/network/kernel/qnetconmonitor_p.h index c6d44ae559d..99acbf4deab 100644 --- a/src/network/kernel/qnetconmonitor_p.h +++ b/src/network/kernel/qnetconmonitor_p.h @@ -73,7 +73,11 @@ public: bool setTargets(const QHostAddress &local, const QHostAddress &remote); bool isReachable(); - // Important: on Darwin you should not call isReachable() after +#ifdef QT_PLATFORM_UIKIT + bool isWwan() const; +#endif + + // Important: on Darwin you should not call isReachable/isWwan() after // startMonitoring(), you have to listen to reachabilityChanged() // signal instead. bool startMonitoring(); @@ -87,6 +91,10 @@ Q_SIGNALS: // callback is coming on a special dispatch queue. void reachabilityChanged(bool isOnline); +#ifdef QT_PLATFORM_UIKIT + void isWwanChanged(bool isWwan); +#endif + private: Q_DECLARE_PRIVATE(QNetworkConnectionMonitor) Q_DISABLE_COPY_MOVE(QNetworkConnectionMonitor) diff --git a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm index 1da671e5492..272327f7da3 100644 --- a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm +++ b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm @@ -69,12 +69,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); @@ -110,10 +118,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(); } @@ -121,9 +135,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, @@ -131,6 +151,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() @@ -143,6 +172,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"