| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- // Copyright 2017 Google
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import <Foundation/Foundation.h>
- #import "Private/FIRReachabilityChecker+Internal.h"
- #import "Private/FIRReachabilityChecker.h"
- #import "Private/FIRLogger.h"
- #import "Private/FIRNetwork.h"
- #import "Private/FIRNetworkMessageCode.h"
- static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
- SCNetworkReachabilityFlags flags,
- void *info);
- static const struct FIRReachabilityApi kFIRDefaultReachabilityApi = {
- SCNetworkReachabilityCreateWithName,
- SCNetworkReachabilitySetCallback,
- SCNetworkReachabilityScheduleWithRunLoop,
- SCNetworkReachabilityUnscheduleFromRunLoop,
- CFRelease,
- };
- static NSString *const kFIRReachabilityUnknownStatus = @"Unknown";
- static NSString *const kFIRReachabilityConnectedStatus = @"Connected";
- static NSString *const kFIRReachabilityDisconnectedStatus = @"Disconnected";
- @interface FIRReachabilityChecker ()
- @property(nonatomic, assign) const struct FIRReachabilityApi *reachabilityApi;
- @property(nonatomic, assign) FIRReachabilityStatus reachabilityStatus;
- @property(nonatomic, copy) NSString *host;
- @property(nonatomic, assign) SCNetworkReachabilityRef reachability;
- @end
- @implementation FIRReachabilityChecker
- @synthesize reachabilityApi = reachabilityApi_;
- @synthesize reachability = reachability_;
- - (const struct FIRReachabilityApi *)reachabilityApi {
- return reachabilityApi_;
- }
- - (void)setReachabilityApi:(const struct FIRReachabilityApi *)reachabilityApi {
- if (reachability_) {
- NSString *message =
- @"Cannot change reachability API while reachability is running. "
- @"Call stop first.";
- [loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
- messageCode:kFIRNetworkMessageCodeReachabilityChecker000
- message:message];
- return;
- }
- reachabilityApi_ = reachabilityApi;
- }
- @synthesize reachabilityStatus = reachabilityStatus_;
- @synthesize host = host_;
- @synthesize reachabilityDelegate = reachabilityDelegate_;
- @synthesize loggerDelegate = loggerDelegate_;
- - (BOOL)isActive {
- return reachability_ != nil;
- }
- - (void)setReachabilityDelegate:(id<FIRReachabilityDelegate>)reachabilityDelegate {
- if (reachabilityDelegate &&
- (![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(FIRReachabilityDelegate)])) {
- FIRLogError(kFIRLoggerCore,
- [NSString stringWithFormat:@"I-NET%06ld",
- (long)kFIRNetworkMessageCodeReachabilityChecker005],
- @"Reachability delegate doesn't conform to Reachability protocol.");
- return;
- }
- reachabilityDelegate_ = reachabilityDelegate;
- }
- - (void)setLoggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate {
- if (loggerDelegate &&
- (![(NSObject *)loggerDelegate conformsToProtocol:@protocol(FIRNetworkLoggerDelegate)])) {
- FIRLogError(kFIRLoggerCore,
- [NSString stringWithFormat:@"I-NET%06ld",
- (long)kFIRNetworkMessageCodeReachabilityChecker006],
- @"Reachability delegate doesn't conform to Logger protocol.");
- return;
- }
- loggerDelegate_ = loggerDelegate;
- }
- - (instancetype)initWithReachabilityDelegate:(id<FIRReachabilityDelegate>)reachabilityDelegate
- loggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate
- withHost:(NSString *)host {
- self = [super init];
- [self setLoggerDelegate:loggerDelegate];
- if (!host || !host.length) {
- [loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
- messageCode:kFIRNetworkMessageCodeReachabilityChecker001
- message:@"Invalid host specified"];
- return nil;
- }
- if (self) {
- [self setReachabilityDelegate:reachabilityDelegate];
- reachabilityApi_ = &kFIRDefaultReachabilityApi;
- reachabilityStatus_ = kFIRReachabilityUnknown;
- host_ = [host copy];
- reachability_ = nil;
- }
- return self;
- }
- - (void)dealloc {
- reachabilityDelegate_ = nil;
- loggerDelegate_ = nil;
- [self stop];
- }
- - (BOOL)start {
- if (!reachability_) {
- reachability_ = reachabilityApi_->createWithNameFn(kCFAllocatorDefault, [host_ UTF8String]);
- if (!reachability_) {
- return NO;
- }
- SCNetworkReachabilityContext context = {
- 0, /* version */
- (__bridge void *)(self), /* info (passed as last parameter to reachability callback) */
- NULL, /* retain */
- NULL, /* release */
- NULL /* copyDescription */
- };
- if (!reachabilityApi_->setCallbackFn(reachability_, ReachabilityCallback, &context) ||
- !reachabilityApi_->scheduleWithRunLoopFn(reachability_, CFRunLoopGetMain(),
- kCFRunLoopCommonModes)) {
- reachabilityApi_->releaseFn(reachability_);
- reachability_ = nil;
- [loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
- messageCode:kFIRNetworkMessageCodeReachabilityChecker002
- message:@"Failed to start reachability handle"];
- return NO;
- }
- }
- [loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
- messageCode:kFIRNetworkMessageCodeReachabilityChecker003
- message:@"Monitoring the network status"];
- return YES;
- }
- - (void)stop {
- if (reachability_) {
- reachabilityStatus_ = kFIRReachabilityUnknown;
- reachabilityApi_->unscheduleFromRunLoopFn(reachability_, CFRunLoopGetMain(),
- kCFRunLoopCommonModes);
- reachabilityApi_->releaseFn(reachability_);
- reachability_ = nil;
- }
- }
- - (FIRReachabilityStatus)statusForFlags:(SCNetworkReachabilityFlags)flags {
- FIRReachabilityStatus status = kFIRReachabilityNotReachable;
- // If the Reachable flag is not set, we definitely don't have connectivity.
- if (flags & kSCNetworkReachabilityFlagsReachable) {
- // Reachable flag is set. Check further flags.
- if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
- // Connection required flag is not set, so we have connectivity.
- #if TARGET_OS_IOS || TARGET_OS_TV
- status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kFIRReachabilityViaCellular
- : kFIRReachabilityViaWifi;
- #elif TARGET_OS_OSX
- status = kFIRReachabilityViaWifi;
- #endif
- } else if ((flags & (kSCNetworkReachabilityFlagsConnectionOnDemand |
- kSCNetworkReachabilityFlagsConnectionOnTraffic)) &&
- !(flags & kSCNetworkReachabilityFlagsInterventionRequired)) {
- // If the connection on demand or connection on traffic flag is set, and user intervention
- // is not required, we have connectivity.
- #if TARGET_OS_IOS || TARGET_OS_TV
- status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kFIRReachabilityViaCellular
- : kFIRReachabilityViaWifi;
- #elif TARGET_OS_OSX
- status = kFIRReachabilityViaWifi;
- #endif
- }
- }
- return status;
- }
- - (void)reachabilityFlagsChanged:(SCNetworkReachabilityFlags)flags {
- FIRReachabilityStatus status = [self statusForFlags:flags];
- if (reachabilityStatus_ != status) {
- NSString *reachabilityStatusString;
- if (status == kFIRReachabilityUnknown) {
- reachabilityStatusString = kFIRReachabilityUnknownStatus;
- } else {
- reachabilityStatusString = (status == kFIRReachabilityNotReachable)
- ? kFIRReachabilityDisconnectedStatus
- : kFIRReachabilityConnectedStatus;
- }
- [loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
- messageCode:kFIRNetworkMessageCodeReachabilityChecker004
- message:@"Network status has changed. Code, status"
- contexts:@[ @(status), reachabilityStatusString ]];
- reachabilityStatus_ = status;
- [reachabilityDelegate_ reachability:self statusChanged:reachabilityStatus_];
- }
- }
- @end
- static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
- SCNetworkReachabilityFlags flags,
- void *info) {
- FIRReachabilityChecker *checker = (__bridge FIRReachabilityChecker *)info;
- [checker reachabilityFlagsChanged:flags];
- }
- // This function used to be at the top of the file, but it was moved here
- // as a workaround for a suspected compiler bug. When compiled in Release mode
- // and run on an iOS device with WiFi disabled, the reachability code crashed
- // when calling SCNetworkReachabilityScheduleWithRunLoop, or shortly thereafter.
- // After unsuccessfully trying to diagnose the cause of the crash, it was
- // discovered that moving this function to the end of the file magically fixed
- // the crash. If you are going to edit this file, exercise caution and make sure
- // to test thoroughly with an iOS device under various network conditions.
- const NSString *FIRReachabilityStatusString(FIRReachabilityStatus status) {
- switch (status) {
- case kFIRReachabilityUnknown:
- return @"Reachability Unknown";
- case kFIRReachabilityNotReachable:
- return @"Not reachable";
- case kFIRReachabilityViaWifi:
- return @"Reachable via Wifi";
- case kFIRReachabilityViaCellular:
- return @"Reachable via Cellular Data";
- default:
- return [NSString stringWithFormat:@"Invalid reachability status %d", (int)status];
- }
- }
|