From 2633bc4e40bfd0eab661cb1c57404e9d6a8babf9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 5 Sep 2022 21:56:54 +0200 Subject: [PATCH 1/2] Add a network reachability monitor class --- .../connection/networkreachabilitymonitor.cpp | 105 ++++++++++++++++ .../connection/networkreachabilitymonitor.h | 34 ++++++ libnymea-app/connection/nymeaconnection.cpp | 112 +++--------------- libnymea-app/connection/nymeaconnection.h | 8 +- libnymea-app/libnymea-app.pri | 2 + 5 files changed, 159 insertions(+), 102 deletions(-) create mode 100644 libnymea-app/connection/networkreachabilitymonitor.cpp create mode 100644 libnymea-app/connection/networkreachabilitymonitor.h diff --git a/libnymea-app/connection/networkreachabilitymonitor.cpp b/libnymea-app/connection/networkreachabilitymonitor.cpp new file mode 100644 index 00000000..b79440b2 --- /dev/null +++ b/libnymea-app/connection/networkreachabilitymonitor.cpp @@ -0,0 +1,105 @@ +#include "networkreachabilitymonitor.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(dcNymeaConnection) + +NetworkReachabilityMonitor::NetworkReachabilityMonitor(QObject *parent) + : QObject{parent} +{ + + m_networkConfigManager = new QNetworkConfigurationManager(this); + + QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){ + Q_UNUSED(config) + qCDebug(dcNymeaConnection()) << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose(); + updateActiveBearers(); + }); + QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){ + Q_UNUSED(config) + qCDebug(dcNymeaConnection()) << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose(); + updateActiveBearers(); + }); + + QGuiApplication *app = static_cast(QGuiApplication::instance()); + QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) { + qCDebug(dcNymeaConnection()) << "Application state changed to:" << state; + + updateActiveBearers(); + }); + + updateActiveBearers(); + +} + +NymeaConnection::BearerTypes NetworkReachabilityMonitor::availableBearerTypes() const +{ + return m_availableBearerTypes; +} + +void NetworkReachabilityMonitor::updateActiveBearers() +{ + NymeaConnection::BearerTypes availableBearerTypes; + QList configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active); + qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count(); + foreach (const QNetworkConfiguration &config, configs) { + qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); + + // NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that... +#if defined(Q_OS_IOS) + availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi); +#else + availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType())); +#endif + } + if (availableBearerTypes == NymeaConnection::BearerTypeNone) { + // This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away... + qCDebug(dcNymeaConnection()) << "No active bearer available. Inactive bearers are:"; + QList configs = m_networkConfigManager->allConfigurations(); + foreach (const QNetworkConfiguration &config, configs) { + qCDebug(dcNymeaConnection()) << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); + } + + qCDebug(dcNymeaConnection()) << "Updating network manager"; + m_networkConfigManager->updateConfigurations(); + } + + if (m_availableBearerTypes != availableBearerTypes) { + qCInfo(dcNymeaConnection()) << "Available Bearer Types changed to:" << availableBearerTypes; + m_availableBearerTypes = availableBearerTypes; + emit availableBearerTypesChanged(); + } else { + qCDebug(dcNymeaConnection()) << "Available Bearer Types:" << availableBearerTypes; + } + + emit availableBearerTypesUpdated(); +} + +NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const +{ + switch (type) { + case QNetworkConfiguration::BearerUnknown: + // Unable to determine the connection type. Assume it's something we can establish any connection type on + return NymeaConnection::BearerTypeAll; + case QNetworkConfiguration::BearerEthernet: + return NymeaConnection::BearerTypeEthernet; + case QNetworkConfiguration::BearerWLAN: + return NymeaConnection::BearerTypeWiFi; + case QNetworkConfiguration::Bearer2G: + case QNetworkConfiguration::BearerCDMA2000: + case QNetworkConfiguration::BearerWCDMA: + case QNetworkConfiguration::BearerHSPA: + case QNetworkConfiguration::BearerWiMAX: + case QNetworkConfiguration::BearerEVDO: + case QNetworkConfiguration::BearerLTE: + case QNetworkConfiguration::Bearer3G: + case QNetworkConfiguration::Bearer4G: + return NymeaConnection::BearerTypeMobileData; + case QNetworkConfiguration::BearerBluetooth: + // Note: Do not confuse this with the Bluetooth transport... For Qt, this means IP over BT, not RFCOMM as we do it. + return NymeaConnection::BearerTypeNone; + } + return NymeaConnection::BearerTypeAll; + +} diff --git a/libnymea-app/connection/networkreachabilitymonitor.h b/libnymea-app/connection/networkreachabilitymonitor.h new file mode 100644 index 00000000..583864b9 --- /dev/null +++ b/libnymea-app/connection/networkreachabilitymonitor.h @@ -0,0 +1,34 @@ +#ifndef NETWORKREACHABILITYMONITOR_H +#define NETWORKREACHABILITYMONITOR_H + +#include +#include + +#include "nymeaconnection.h" + +class NetworkReachabilityMonitor : public QObject +{ + Q_OBJECT + Q_PROPERTY(NymeaConnection::BearerTypes availableBearerTypes READ availableBearerTypes NOTIFY availableBearerTypesChanged) +public: + explicit NetworkReachabilityMonitor(QObject *parent = nullptr); + + NymeaConnection::BearerTypes availableBearerTypes() const; + +signals: + void availableBearerTypesChanged(); + void availableBearerTypesUpdated(); // Does not necessarily mean they changed, but they're reasonably up to date now. + +private slots: + void updateActiveBearers(); + +private: + NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const; + +private: + QNetworkConfigurationManager *m_networkConfigManager = nullptr; + NymeaConnection::BearerTypes m_availableBearerTypes = NymeaConnection::BearerTypeNone; + +}; + +#endif // NETWORKREACHABILITYMONITOR_H diff --git a/libnymea-app/connection/nymeaconnection.cpp b/libnymea-app/connection/nymeaconnection.cpp index 2c8a17b1..24f32789 100644 --- a/libnymea-app/connection/nymeaconnection.cpp +++ b/libnymea-app/connection/nymeaconnection.cpp @@ -43,6 +43,7 @@ #include #include +#include "networkreachabilitymonitor.h" #include "nymeatransportinterface.h" #include "logging.h" @@ -50,18 +51,9 @@ NYMEA_LOGGING_CATEGORY(dcNymeaConnection, "NymeaConnection") NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) { - m_networkConfigManager = new QNetworkConfigurationManager(this); - - QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){ - Q_UNUSED(config) - qCDebug(dcNymeaConnection()) << "Network configuration added:" << config.name() << config.bearerTypeName() << config.purpose(); - updateActiveBearers(); - }); - QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationRemoved, this, [this](const QNetworkConfiguration &config){ - Q_UNUSED(config) - qCDebug(dcNymeaConnection()) << "Network configuration removed:" << config.name() << config.bearerTypeName() << config.purpose(); - updateActiveBearers(); - }); + m_networkReachabilityMonitor = new NetworkReachabilityMonitor(this); + connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, &NymeaConnection::availableBearerTypesChanged); + connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesUpdated, this, &NymeaConnection::onAvailableBearerTypesUpdated); QGuiApplication *app = static_cast(QGuiApplication::instance()); QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) { @@ -79,12 +71,8 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) } } } - - updateActiveBearers(); }); - updateActiveBearers(); - m_reconnectTimer.setInterval(500); m_reconnectTimer.setSingleShot(true); connect(&m_reconnectTimer, &QTimer::timeout, this, [this](){ @@ -108,7 +96,7 @@ NymeaConnection::~NymeaConnection() NymeaConnection::BearerTypes NymeaConnection::availableBearerTypes() const { - return m_availableBearerTypes; + return m_networkReachabilityMonitor->availableBearerTypes(); } bool NymeaConnection::connected() @@ -391,41 +379,8 @@ void NymeaConnection::onDataAvailable(const QByteArray &data) } } -void NymeaConnection::updateActiveBearers() +void NymeaConnection::onAvailableBearerTypesUpdated() { - NymeaConnection::BearerTypes availableBearerTypes; - QList configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active); - qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count(); - foreach (const QNetworkConfiguration &config, configs) { - qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); - - // NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that... -#if defined(Q_OS_IOS) - availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi); -#else - availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType())); -#endif - } - if (availableBearerTypes == NymeaConnection::BearerTypeNone) { - // This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away... - qCDebug(dcNymeaConnection()) << "No active bearer available. Inactive bearers are:"; - QList configs = m_networkConfigManager->allConfigurations(); - foreach (const QNetworkConfiguration &config, configs) { - qCDebug(dcNymeaConnection()) << "Inactive network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); - } - - qCDebug(dcNymeaConnection()) << "Updating network manager"; - m_networkConfigManager->updateConfigurations(); - } - - if (m_availableBearerTypes != availableBearerTypes) { - qCInfo(dcNymeaConnection()) << "Available Bearer Types changed to:" << availableBearerTypes; - m_availableBearerTypes = availableBearerTypes; - emit availableBearerTypesChanged(); - } else { - qCDebug(dcNymeaConnection()) << "Available Bearer Types:" << availableBearerTypes; - } - if (!m_currentHost) { // No host set... Nothing to do... qCInfo(dcNymeaConnection()) << "No current host... Nothing to do..."; @@ -490,8 +445,8 @@ void NymeaConnection::connectInternal(NymeaHost *host) qCDebug(dcNymeaConnection()) << "Best candidate Loopback connection:" << loopbackConnection->url(); connectInternal(loopbackConnection); - } else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeWiFi) - || m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeEthernet)) { + } else if (m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeWiFi) + || m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeEthernet)) { Connection* lanConnection = host->connections()->bestMatch(Connection::BearerTypeLan | Connection::BearerTypeWan); if (lanConnection) { qCDebug(dcNymeaConnection()) << "Best candidate LAN/WAN connection:" << lanConnection->url(); @@ -500,7 +455,7 @@ void NymeaConnection::connectInternal(NymeaHost *host) qCDebug(dcNymeaConnection()) << "No available LAN/WAN connection to" << host->name(); } - } else if (m_availableBearerTypes.testFlag(NymeaConnection::BearerTypeMobileData)) { + } else if (m_networkReachabilityMonitor->availableBearerTypes().testFlag(NymeaConnection::BearerTypeMobileData)) { Connection* wanConnection = host->connections()->bestMatch(Connection::BearerTypeWan); if (wanConnection) { qCDebug(dcNymeaConnection()) << "Best candidate WAN connection:" << wanConnection->url(); @@ -547,61 +502,24 @@ bool NymeaConnection::connectInternal(Connection *connection) QObject::connect(newTransport, &NymeaTransportInterface::disconnected, this, &NymeaConnection::onDisconnected); QObject::connect(newTransport, &NymeaTransportInterface::dataReady, this, &NymeaConnection::onDataAvailable, Qt::QueuedConnection); -// // Load any certificate we might have for this url -// QByteArray pem; -// if (loadPem(connection->url(), pem)) { -// qDebug() << "Loaded SSL certificate for" << connection->url().host(); -// QList expectedSslErrors; -// expectedSslErrors.append(QSslError::HostNameMismatch); -// expectedSslErrors.append(QSslError(QSslError::SelfSignedCertificate, QSslCertificate(pem))); -// newTransport->ignoreSslErrors(expectedSslErrors); -// } - m_transportCandidates.insert(newTransport, connection); qCInfo(dcNymeaConnection()) << "Connecting to:" << connection->url() << newTransport << m_transportCandidates.value(newTransport); return newTransport->connect(connection->url()); } -NymeaConnection::BearerType NymeaConnection::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const -{ - switch (type) { - case QNetworkConfiguration::BearerUnknown: - // Unable to determine the connection type. Assume it's something we can establish any connection type on - return BearerTypeAll; - case QNetworkConfiguration::BearerEthernet: - return BearerTypeEthernet; - case QNetworkConfiguration::BearerWLAN: - return BearerTypeWiFi; - case QNetworkConfiguration::Bearer2G: - case QNetworkConfiguration::BearerCDMA2000: - case QNetworkConfiguration::BearerWCDMA: - case QNetworkConfiguration::BearerHSPA: - case QNetworkConfiguration::BearerWiMAX: - case QNetworkConfiguration::BearerEVDO: - case QNetworkConfiguration::BearerLTE: - case QNetworkConfiguration::Bearer3G: - case QNetworkConfiguration::Bearer4G: - return BearerTypeMobileData; - case QNetworkConfiguration::BearerBluetooth: - // Note: Do not confuse this with the Bluetooth transport... For Qt, this means IP over BT, not RFCOMM as we do it. - return BearerTypeNone; - } - return BearerTypeAll; -} - bool NymeaConnection::isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const { switch (connectionBearerType) { case Connection::BearerTypeLan: - return m_availableBearerTypes.testFlag(BearerTypeEthernet) - || m_availableBearerTypes.testFlag(BearerTypeWiFi); + return availableBearerTypes().testFlag(BearerTypeEthernet) + || availableBearerTypes().testFlag(BearerTypeWiFi); case Connection::BearerTypeWan: case Connection::BearerTypeCloud: - return m_availableBearerTypes.testFlag(BearerTypeEthernet) - || m_availableBearerTypes.testFlag(BearerTypeWiFi) - || m_availableBearerTypes.testFlag(BearerTypeMobileData); + return availableBearerTypes().testFlag(BearerTypeEthernet) + || availableBearerTypes().testFlag(BearerTypeWiFi) + || availableBearerTypes().testFlag(BearerTypeMobileData); case Connection::BearerTypeBluetooth: - return m_availableBearerTypes.testFlag(BearerTypeBluetooth); + return availableBearerTypes().testFlag(BearerTypeBluetooth); case Connection::BearerTypeUnknown: return true; case Connection::BearerTypeNone: diff --git a/libnymea-app/connection/nymeaconnection.h b/libnymea-app/connection/nymeaconnection.h index 74233e58..994b9072 100644 --- a/libnymea-app/connection/nymeaconnection.h +++ b/libnymea-app/connection/nymeaconnection.h @@ -43,6 +43,7 @@ class NymeaTransportInterface; class NymeaTransportInterfaceFactory; +class NetworkReachabilityMonitor; class NymeaConnection : public QObject { @@ -121,20 +122,17 @@ private slots: void onDisconnected(); void onDataAvailable(const QByteArray &data); - void updateActiveBearers(); + void onAvailableBearerTypesUpdated(); void hostConnectionsUpdated(); private: void connectInternal(NymeaHost *host); bool connectInternal(Connection *connection); - NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const; - bool isConnectionBearerAvailable(Connection::BearerType connectionBearerType) const; private: ConnectionStatus m_connectionStatus = ConnectionStatusUnconnected; - QNetworkConfigurationManager *m_networkConfigManager = nullptr; - NymeaConnection::BearerTypes m_availableBearerTypes = BearerTypeNone; + NetworkReachabilityMonitor *m_networkReachabilityMonitor = nullptr; QHash m_transportFactories; QHash m_transportCandidates; diff --git a/libnymea-app/libnymea-app.pri b/libnymea-app/libnymea-app.pri index f67f930c..85b31501 100644 --- a/libnymea-app/libnymea-app.pri +++ b/libnymea-app/libnymea-app.pri @@ -21,6 +21,7 @@ INCLUDEPATH += \ SOURCES += \ $$PWD/appdata.cpp \ + $$PWD/connection/networkreachabilitymonitor.cpp \ $$PWD/energy/energylogs.cpp \ $$PWD/energy/energymanager.cpp \ $$PWD/energy/powerbalancelogs.cpp \ @@ -186,6 +187,7 @@ SOURCES += \ HEADERS += \ $$PWD/appdata.h \ + $$PWD/connection/networkreachabilitymonitor.h \ $$PWD/energy/energylogs.h \ $$PWD/energy/energymanager.h \ $$PWD/energy/powerbalancelogs.h \ From dee1aa4e042c4c3798f4ec30769da4d3e6f6d3f7 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 6 Sep 2022 13:25:39 +0200 Subject: [PATCH 2/2] Add iOS specific implementation for the networkreachabilitymonitor --- .../connection/networkreachabilitymonitor.cpp | 18 +-- .../connection/networkreachabilitymonitor.h | 15 ++- .../networkreachabilitymonitorios.mm | 103 ++++++++++++++++++ libnymea-app/connection/nymeaconnection.cpp | 8 ++ libnymea-app/libnymea-app.pri | 4 + 5 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 libnymea-app/connection/networkreachabilitymonitorios.mm diff --git a/libnymea-app/connection/networkreachabilitymonitor.cpp b/libnymea-app/connection/networkreachabilitymonitor.cpp index b79440b2..0d57bc35 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.cpp +++ b/libnymea-app/connection/networkreachabilitymonitor.cpp @@ -9,6 +9,10 @@ NetworkReachabilityMonitor::NetworkReachabilityMonitor(QObject *parent) : QObject{parent} { +#if defined(Q_OS_IOS) + setupIOS(); +#endif + m_networkConfigManager = new QNetworkConfigurationManager(this); QObject::connect(m_networkConfigManager, &QNetworkConfigurationManager::configurationAdded, this, [this](const QNetworkConfiguration &config){ @@ -40,18 +44,16 @@ NymeaConnection::BearerTypes NetworkReachabilityMonitor::availableBearerTypes() void NetworkReachabilityMonitor::updateActiveBearers() { + // NOTE: The Qt API is not working at all on iOS, we're using the iOS reachability API instead. +#if defined(Q_OS_IOS) + return; +#endif + NymeaConnection::BearerTypes availableBearerTypes; QList configs = m_networkConfigManager->allConfigurations(QNetworkConfiguration::Active); qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count(); foreach (const QNetworkConfiguration &config, configs) { qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); - - // NOTE: iOS doesn't correctly report bearer types. It'll be Unknown all the time. Let's hardcode it to WiFi for that... -#if defined(Q_OS_IOS) - availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi); -#else - availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType())); -#endif } if (availableBearerTypes == NymeaConnection::BearerTypeNone) { // This is just debug info... On some platform bearer management seems a bit broken, so let's get some infos right away... @@ -76,7 +78,7 @@ void NetworkReachabilityMonitor::updateActiveBearers() emit availableBearerTypesUpdated(); } -NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const +NymeaConnection::BearerType NetworkReachabilityMonitor::qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) { switch (type) { case QNetworkConfiguration::BearerUnknown: diff --git a/libnymea-app/connection/networkreachabilitymonitor.h b/libnymea-app/connection/networkreachabilitymonitor.h index 583864b9..95a526ca 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.h +++ b/libnymea-app/connection/networkreachabilitymonitor.h @@ -6,6 +6,10 @@ #include "nymeaconnection.h" +#ifdef Q_OS_IOS +#import +#endif + class NetworkReachabilityMonitor : public QObject { Q_OBJECT @@ -22,13 +26,18 @@ signals: private slots: void updateActiveBearers(); -private: - NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type) const; - private: QNetworkConfigurationManager *m_networkConfigManager = nullptr; NymeaConnection::BearerTypes m_availableBearerTypes = NymeaConnection::BearerTypeNone; + static NymeaConnection::BearerType qBearerTypeToNymeaBearerType(QNetworkConfiguration::BearerType type); + +#ifdef Q_OS_IOS + void setupIOS(); + SCNetworkReachabilityRef _reachabilityRef; + static NymeaConnection::BearerType flagsToBearerType(SCNetworkReachabilityFlags flags); + static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info); +#endif }; #endif // NETWORKREACHABILITYMONITOR_H diff --git a/libnymea-app/connection/networkreachabilitymonitorios.mm b/libnymea-app/connection/networkreachabilitymonitorios.mm new file mode 100644 index 00000000..a0c4f6c7 --- /dev/null +++ b/libnymea-app/connection/networkreachabilitymonitorios.mm @@ -0,0 +1,103 @@ +#include "networkreachabilitymonitor.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#include + +void NetworkReachabilityMonitor::ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target, flags) + NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); + + NetworkReachabilityMonitor* thiz = (__bridge NetworkReachabilityMonitor *)info; + // Post a notification to notify the client that the network reachability changed. + qCritical() << "******* network reachability changed"; + NymeaConnection::BearerTypes old = thiz->m_availableBearerTypes; + thiz->m_availableBearerTypes = flagsToBearerType(flags); + if (thiz->m_availableBearerTypes != old) { + emit thiz->availableBearerTypesChanged(); + } + emit thiz->availableBearerTypesUpdated(); +} + +void NetworkReachabilityMonitor::setupIOS() +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); + if (reachability != NULL) { + _reachabilityRef = reachability; + } + + SCNetworkReachabilityContext context = {0, (__bridge void *)(this), NULL, NULL, NULL}; + qCritical() << "Registering callback"; + if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) { + qCritical() << "Callback registered"; + if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + qCritical() << "******* reachability callback set up"; + } else { + qCritical() << "******** Error setting up reachability callback"; + } + } + + + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) { + m_availableBearerTypes = flagsToBearerType(flags); + } + + // TODO: unregister +// SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + +} + +NymeaConnection::BearerType NetworkReachabilityMonitor::flagsToBearerType(SCNetworkReachabilityFlags flags) +{ + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) { + return NymeaConnection::BearerTypeNone; + } + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) { + /* + If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... + */ + return NymeaConnection::BearerTypeWiFi; + } + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) { + /* + ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... + */ + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) + { + /* + ... and no [user] intervention is needed... + */ + return NymeaConnection::BearerTypeWiFi; + } + } + + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) + { + /* + ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. + */ + return NymeaConnection::BearerTypeMobileData; + } + + return NymeaConnection::BearerTypeNone; +} diff --git a/libnymea-app/connection/nymeaconnection.cpp b/libnymea-app/connection/nymeaconnection.cpp index 24f32789..fe7d327f 100644 --- a/libnymea-app/connection/nymeaconnection.cpp +++ b/libnymea-app/connection/nymeaconnection.cpp @@ -55,6 +55,14 @@ NymeaConnection::NymeaConnection(QObject *parent) : QObject(parent) connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, &NymeaConnection::availableBearerTypesChanged); connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesUpdated, this, &NymeaConnection::onAvailableBearerTypesUpdated); +#ifdef Q_OS_IOS + connect(m_networkReachabilityMonitor, &NetworkReachabilityMonitor::availableBearerTypesChanged, this, [this](){ + if (m_currentTransport) { + m_currentTransport->disconnect(); + } + }); +#endif + QGuiApplication *app = static_cast(QGuiApplication::instance()); QObject::connect(app, &QGuiApplication::applicationStateChanged, this, [app, this](Qt::ApplicationState state) { qCDebug(dcNymeaConnection()) << "Application state changed to:" << state; diff --git a/libnymea-app/libnymea-app.pri b/libnymea-app/libnymea-app.pri index 85b31501..02fd0c2b 100644 --- a/libnymea-app/libnymea-app.pri +++ b/libnymea-app/libnymea-app.pri @@ -358,3 +358,7 @@ ubports: { android: { DESTDIR = $${ANDROID_TARGET_ARCH} } + +ios: { + OBJECTIVE_SOURCES += $${PWD}/connection/networkreachabilitymonitorios.mm +}