From 2633bc4e40bfd0eab661cb1c57404e9d6a8babf9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Mon, 5 Sep 2022 21:56:54 +0200 Subject: [PATCH] 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 \