diff --git a/libnymea-app/connection/networkreachabilitymonitor.cpp b/libnymea-app/connection/networkreachabilitymonitor.cpp index c910acd7..2bdc5414 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.cpp +++ b/libnymea-app/connection/networkreachabilitymonitor.cpp @@ -8,7 +8,8 @@ Q_DECLARE_LOGGING_CATEGORY(dcNymeaConnection) NetworkReachabilityMonitor::NetworkReachabilityMonitor(QObject *parent) : QObject{parent} { - + // NOTE: The Qt API is not working at all on iOS, we're using the iOS reachability API instead. + // See iOS implementation in .mm file #if defined(Q_OS_IOS) setupIOS(); #endif @@ -51,7 +52,6 @@ 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 @@ -61,6 +61,7 @@ void NetworkReachabilityMonitor::updateActiveBearers() qCDebug(dcNymeaConnection()) << "Network configuations:" << configs.count(); foreach (const QNetworkConfiguration &config, configs) { qCDebug(dcNymeaConnection()) << "Active network config:" << config.name() << config.bearerTypeFamily() << config.bearerTypeName(); + availableBearerTypes.setFlag(qBearerTypeToNymeaBearerType(config.bearerType())); } 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... diff --git a/libnymea-app/connection/networkreachabilitymonitor.h b/libnymea-app/connection/networkreachabilitymonitor.h index 56cb8d61..7f0152d1 100644 --- a/libnymea-app/connection/networkreachabilitymonitor.h +++ b/libnymea-app/connection/networkreachabilitymonitor.h @@ -36,8 +36,8 @@ private: #ifdef Q_OS_IOS void setupIOS(); void teardownIOS(); - SCNetworkReachabilityRef m_reachabilityRef; - static NymeaConnection::BearerType flagsToBearerType(SCNetworkReachabilityFlags flags); + SCNetworkReachabilityRef m_internetReachabilityRef = nullptr; + SCNetworkReachabilityRef m_lanReachabilityRef = nullptr; static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info); #endif }; diff --git a/libnymea-app/connection/networkreachabilitymonitorios.mm b/libnymea-app/connection/networkreachabilitymonitorios.mm index b13ca9bd..452a1d49 100644 --- a/libnymea-app/connection/networkreachabilitymonitorios.mm +++ b/libnymea-app/connection/networkreachabilitymonitorios.mm @@ -15,87 +15,105 @@ Q_DECLARE_LOGGING_CATEGORY(dcNymeaConnection) 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. - NymeaConnection::BearerTypes old = thiz->m_availableBearerTypes; - thiz->m_availableBearerTypes = flagsToBearerType(flags); - qCDebug(dcNymeaConnection()) << "Network reachability changed. Old bearers:" << old << "New bearers:" << thiz->m_availableBearerTypes << "(Flags:" << flags << ")"; - if (thiz->m_availableBearerTypes != old) { + + NymeaConnection::BearerTypes newTypes = thiz->m_availableBearerTypes; + + if (target == thiz->m_internetReachabilityRef) { + // If the internet reachability changes, enable the mobile data bearer if we're reaching the internet through mobile data + newTypes.setFlag(NymeaConnection::BearerTypeMobileData, (flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsWWAN)); + qCDebug(dcNymeaConnection()) << "Internet reachability changed"; + } else if (target == thiz->m_lanReachabilityRef) { + // If the lan reachability changes, we'll enable the wifi bearer, regardless of how + newTypes.setFlag(NymeaConnection::BearerTypeWiFi, flags & kSCNetworkReachabilityFlagsReachable); + qCDebug(dcNymeaConnection()) << "LAN reachability changed"; + } + + qCDebug(dcNymeaConnection()) << "Old bearers:" << thiz->m_availableBearerTypes << QString("new network reachability flags: %1%2 %3%4%5%6%7%8%9") + .arg((flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-') + .arg((flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-') + + .arg((flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-') + .arg((flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-') + .arg((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-') + .arg((flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-') + .arg((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-') + .arg((flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-') + .arg((flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-') + << "new bearers:" << newTypes; + + if (thiz->m_availableBearerTypes != newTypes) { + thiz->m_availableBearerTypes = newTypes; emit thiz->availableBearerTypesChanged(); } emit thiz->availableBearerTypesUpdated(); + } void NetworkReachabilityMonitor::setupIOS() { + SCNetworkReachabilityContext context = {0, (__bridge void *)(this), NULL, NULL, NULL}; + struct sockaddr_in zeroAddress; bzero(&zeroAddress, sizeof(zeroAddress)); zeroAddress.sin_len = sizeof(zeroAddress); zeroAddress.sin_family = AF_INET; - m_reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); - if (m_reachabilityRef == NULL) { - qCCritical(dcNymeaConnection()) << "Error setting up reachability monitor"; - return; - } + m_internetReachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); - SCNetworkReachabilityContext context = {0, (__bridge void *)(this), NULL, NULL, NULL}; - if (SCNetworkReachabilitySetCallback(m_reachabilityRef, ReachabilityCallback, &context)) { - if (SCNetworkReachabilityScheduleWithRunLoop(m_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { - } else { - qCCritical(dcNymeaConnection()) << "Error setting up reachability callback"; + if (m_internetReachabilityRef) { + if (SCNetworkReachabilitySetCallback(m_internetReachabilityRef, ReachabilityCallback, &context)) { + if (SCNetworkReachabilityScheduleWithRunLoop(m_internetReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + } else { + qCCritical(dcNymeaConnection()) << "Error setting up internet reachability callback (runloop)"; + } } + + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(m_internetReachabilityRef, &flags)) { + m_availableBearerTypes.setFlag(NymeaConnection::BearerTypeMobileData, (flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsWWAN)); + } + } else { + qCCritical(dcNymeaConnection()) << "Error setting up internet reachability monitor (register)"; } - SCNetworkReachabilityFlags flags; - if (SCNetworkReachabilityGetFlags(m_reachabilityRef, &flags)) { - m_availableBearerTypes = flagsToBearerType(flags); + + struct sockaddr_in linkLocalAddress; + bzero(&linkLocalAddress, sizeof(linkLocalAddress)); + linkLocalAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + linkLocalAddress.sin_len = sizeof(linkLocalAddress); + linkLocalAddress.sin_family = AF_INET; + + m_lanReachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&linkLocalAddress); + + if (m_lanReachabilityRef) { + if (SCNetworkReachabilitySetCallback(m_lanReachabilityRef, ReachabilityCallback, &context)) { + if (SCNetworkReachabilityScheduleWithRunLoop(m_lanReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + } else { + qCCritical(dcNymeaConnection()) << "Error setting up LAN reachability callback (runloop)"; + } + } + + SCNetworkReachabilityFlags flags; + if (SCNetworkReachabilityGetFlags(m_lanReachabilityRef, &flags)) { + m_availableBearerTypes.setFlag(NymeaConnection::BearerTypeWiFi, flags & kSCNetworkReachabilityFlagsReachable); + } + } else { + qCCritical(dcNymeaConnection()) << "Error setting up LAN reachability monitor (register)"; } + } void NetworkReachabilityMonitor::teardownIOS() { - SCNetworkReachabilityUnscheduleFromRunLoop(m_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; + if (m_internetReachabilityRef) { + SCNetworkReachabilityUnscheduleFromRunLoop(m_internetReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(m_internetReachabilityRef); + m_internetReachabilityRef = nullptr; + } + if (m_lanReachabilityRef) { + SCNetworkReachabilityUnscheduleFromRunLoop(m_lanReachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(m_lanReachabilityRef); + m_lanReachabilityRef = nullptr; + } }