| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 | /* * * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * *     * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. *     * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. *     * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */#import "GRPCConnectivityMonitor.h"#pragma mark Flags@implementation GRPCReachabilityFlags {  SCNetworkReachabilityFlags _flags;}+ (instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags {  return [[self alloc] initWithFlags:flags];}- (instancetype)initWithFlags:(SCNetworkReachabilityFlags)flags {  if ((self = [super init])) {    _flags = flags;  }  return self;}/* * One accessor method implementation per flag. Example:- (BOOL)isCell { \  return !!(_flags & kSCNetworkReachabilityFlagsIsWWAN); \} */#define GRPC_XMACRO_ITEM(methodName, FlagName) \- (BOOL)methodName { \  return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \}#include "GRPCReachabilityFlagNames.xmacro.h"#undef GRPC_XMACRO_ITEM- (BOOL)isHostReachable {  // Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs  // on top of it.  // connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on  // demand).  return self.reachable && !self.interventionRequired && !self.connectionOnDemand;}- (NSString *)description {  NSMutableArray *activeOptions = [NSMutableArray arrayWithCapacity:9];  /*   * For each flag, add its name to the array if it's ON. Example:  if (self.isCell) {    [activeOptions addObject:@"isCell"];  }   */  #define GRPC_XMACRO_ITEM(methodName, FlagName) \    if (self.methodName) {                       \      [activeOptions addObject:@ #methodName];   \    }  #include "GRPCReachabilityFlagNames.xmacro.h"  #undef GRPC_XMACRO_ITEM  return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "];}- (BOOL)isEqual:(id)object {  return [object isKindOfClass:[GRPCReachabilityFlags class]] &&      _flags == ((GRPCReachabilityFlags *)object)->_flags;}- (NSUInteger)hash {  return _flags;}@end#pragma mark Connectivity Monitor// Assumes the third argument is a block that accepts a GRPCReachabilityFlags object, and passes the// received ones to it.static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target,                                        SCNetworkReachabilityFlags flags,                                        void *info) {  #pragma unused (target)  // This can be called many times with the same info. The info is retained by SCNetworkReachability  // while this function is being executed.  void (^handler)(GRPCReachabilityFlags *) = (__bridge void (^)(GRPCReachabilityFlags *))info;  handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]);}@implementation GRPCConnectivityMonitor {  SCNetworkReachabilityRef _reachabilityRef;  GRPCReachabilityFlags *_previousReachabilityFlags;}- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability {  if (!reachability) {    return nil;  }  if ((self = [super init])) {    _reachabilityRef = CFRetain(reachability);    _queue = dispatch_get_main_queue();    _previousReachabilityFlags = nil;  }  return self;}+ (nullable instancetype)monitorWithHost:(nonnull NSString *)host {  const char *hostName = host.UTF8String;  if (!hostName) {    [NSException raise:NSInvalidArgumentException                format:@"host.UTF8String returns NULL for %@", host];  }  SCNetworkReachabilityRef reachability =      SCNetworkReachabilityCreateWithName(NULL, hostName);  GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability];  if (reachability) {    CFRelease(reachability);  }  return returnValue;}- (void)handleLossWithHandler:(nullable void (^)())lossHandler      wifiStatusChangeHandler:(nullable void (^)())wifiStatusChangeHandler {  __weak typeof(self) weakSelf = self;  [self startListeningWithHandler:^(GRPCReachabilityFlags *flags) {    typeof(self) strongSelf = weakSelf;    if (strongSelf) {      if (lossHandler && !flags.reachable) {        lossHandler();      } else if (wifiStatusChangeHandler &&                 strongSelf->_previousReachabilityFlags &&                 (flags.isWWAN ^                  strongSelf->_previousReachabilityFlags.isWWAN)) {        wifiStatusChangeHandler();      }      strongSelf->_previousReachabilityFlags = flags;    }  }];}- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler {  // Copy to ensure the handler block is in the heap (and so can't be deallocated when this method  // returns).  void (^copiedHandler)(GRPCReachabilityFlags *) = [handler copy];  SCNetworkReachabilityContext context = {    .version = 0,    .info = (__bridge void *)copiedHandler,    .retain = CFRetain,    .release = CFRelease,  };  // The following will retain context.info, and release it when the callback is set to NULL.  SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context);  SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue);}- (void)stopListening {  // This releases the block on context.info.  SCNetworkReachabilitySetCallback(_reachabilityRef, NULL, NULL);  SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL);}- (void)setQueue:(dispatch_queue_t)queue {  _queue = queue ?: dispatch_get_main_queue();}- (void)dealloc {  if (_reachabilityRef) {    [self stopListening];    CFRelease(_reachabilityRef);  }}@end
 |