1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "RNCConnectionState.h"
9#if !TARGET_OS_TV && !TARGET_OS_MACCATALYST
10#import <CoreTelephony/CTTelephonyNetworkInfo.h>
11#endif
12
13#if TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
14#include <ifaddrs.h>
15#endif
16
17@implementation RNCConnectionState
18
19// Creates a new "blank" state
20- (instancetype)init
21{
22    self = [super init];
23    if (self) {
24        _type = RNCConnectionTypeUnknown;
25        _cellularGeneration = nil;
26        _expensive = false;
27    }
28    return self;
29}
30
31// Creates the state from the given reachability references
32- (instancetype)initWithReachabilityFlags:(SCNetworkReachabilityFlags)flags
33{
34    self = [self init];
35    if (self) {
36        _type = RNCConnectionTypeUnknown;
37        _cellularGeneration = nil;
38        _expensive = false;
39
40        if ((flags & kSCNetworkReachabilityFlagsReachable) == 0 ||
41            (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0) {
42            _type = RNCConnectionTypeNone;
43        }
44#if !TARGET_OS_TV && !TARGET_OS_OSX && !TARGET_OS_MACCATALYST
45        else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
46            _type = RNCConnectionTypeCellular;
47            _expensive = true;
48
49            CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
50            if (netinfo) {
51                if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
52                    [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
53                    [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
54                    _cellularGeneration = RNCCellularGeneration2g;
55                } else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
56                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
57                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
58                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
59                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
60                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] ||
61                           [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
62                    _cellularGeneration = RNCCellularGeneration3g;
63                } else if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
64                    _cellularGeneration = RNCCellularGeneration4g;
65                } else if (@available(iOS 14.1, *)) {
66                    if ([netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyNRNSA] ||
67                        [netinfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyNR]) {
68                        _cellularGeneration = RNCCellularGeneration5g;
69                    }
70                }
71            }
72        }
73#endif
74        else {
75            _type = RNCConnectionTypeWifi;
76#if TARGET_OS_TV || TARGET_OS_OSX || TARGET_OS_MACCATALYST
77            struct ifaddrs *interfaces = NULL;
78            struct ifaddrs *temp_addr = NULL;
79            int success = 0;
80            // retrieve the current interfaces - returns 0 on success
81            success = getifaddrs(&interfaces);
82            if (success == 0) {
83                // Loop through linked list of interfaces
84                temp_addr = interfaces;
85                while (temp_addr != NULL) {
86                    if (temp_addr->ifa_addr->sa_family == AF_INET) {
87                        // Check if interface is en0 which is the ethernet connection on the Apple TV
88                        NSString* ifname = [NSString stringWithUTF8String:temp_addr->ifa_name];
89                        if ([ifname isEqualToString:@"en0"]) {
90                            _type = RNCConnectionTypeEthernet;
91                        }
92                    }
93                    temp_addr = temp_addr->ifa_next;
94                }
95            }
96            // Free memory
97            freeifaddrs(interfaces);
98#endif
99        }
100    }
101    return self;
102}
103
104// Checks if two states are equal
105- (BOOL)isEqualToConnectionState:(RNCConnectionState *)otherState
106{
107    return [self.type isEqualToString:otherState.type]
108            && [self.cellularGeneration isEqualToString:otherState.cellularGeneration]
109            && self.expensive == otherState.expensive;
110}
111
112- (BOOL)connected
113{
114    return ![self.type isEqualToString:RNCConnectionTypeNone] && ![self.type isEqualToString:RNCConnectionTypeUnknown];
115}
116
117@end
118