OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/network_change_notifier_mac.h" | 5 #include "net/base/network_change_notifier_mac.h" |
6 | 6 |
7 #include <netinet/in.h> | 7 #include <netinet/in.h> |
8 #include <resolv.h> | 8 #include <resolv.h> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
11 #include "base/threading/thread.h" | 11 #include "base/threading/thread.h" |
12 #include "net/dns/dns_config_service.h" | 12 #include "net/dns/dns_config_service.h" |
13 | 13 |
14 namespace net { | 14 namespace net { |
15 | 15 |
16 static bool CalculateReachability(SCNetworkConnectionFlags flags) { | 16 static bool CalculateReachability(SCNetworkConnectionFlags flags) { |
17 bool reachable = flags & kSCNetworkFlagsReachable; | 17 bool reachable = flags & kSCNetworkFlagsReachable; |
18 bool connection_required = flags & kSCNetworkFlagsConnectionRequired; | 18 bool connection_required = flags & kSCNetworkFlagsConnectionRequired; |
19 return reachable && !connection_required; | 19 return reachable && !connection_required; |
20 } | 20 } |
21 | 21 |
22 NetworkChangeNotifier::ConnectionType CalculateConnectionType( | 22 NetworkChangeNotifier::ConnectionType CalculateConnectionType( |
23 SCNetworkConnectionFlags flags) { | 23 SCNetworkConnectionFlags flags) { |
24 bool reachable = CalculateReachability(flags); | 24 bool reachable = CalculateReachability(flags); |
25 if (reachable) { | 25 if (reachable) { |
26 #if defined(OS_IOS) | 26 #if defined(OS_IOS) |
27 return (flags & kSCNetworkReachabilityFlagsIsWWAN) ? | 27 return (flags & kSCNetworkReachabilityFlagsIsWWAN) |
28 NetworkChangeNotifier::CONNECTION_3G : | 28 ? NetworkChangeNotifier::CONNECTION_3G |
29 NetworkChangeNotifier::CONNECTION_WIFI; | 29 : NetworkChangeNotifier::CONNECTION_WIFI; |
30 #else | 30 #else |
31 // TODO(droger): Get something more detailed than CONNECTION_UNKNOWN. | 31 // TODO(droger): Get something more detailed than CONNECTION_UNKNOWN. |
32 // http://crbug.com/112937 | 32 // http://crbug.com/112937 |
33 return NetworkChangeNotifier::CONNECTION_UNKNOWN; | 33 return NetworkChangeNotifier::CONNECTION_UNKNOWN; |
34 #endif // defined(OS_IOS) | 34 #endif // defined(OS_IOS) |
35 } else { | 35 } else { |
36 return NetworkChangeNotifier::CONNECTION_NONE; | 36 return NetworkChangeNotifier::CONNECTION_NONE; |
37 } | 37 } |
38 } | 38 } |
39 | 39 |
40 // Thread on which we can run DnsConfigService, which requires a TYPE_IO | 40 // Thread on which we can run DnsConfigService, which requires a TYPE_IO |
41 // message loop. | 41 // message loop. |
42 class NetworkChangeNotifierMac::DnsConfigServiceThread : public base::Thread { | 42 class NetworkChangeNotifierMac::DnsConfigServiceThread : public base::Thread { |
43 public: | 43 public: |
44 DnsConfigServiceThread() : base::Thread("DnsConfigService") {} | 44 DnsConfigServiceThread() : base::Thread("DnsConfigService") {} |
45 | 45 |
46 virtual ~DnsConfigServiceThread() { | 46 virtual ~DnsConfigServiceThread() { Stop(); } |
47 Stop(); | |
48 } | |
49 | 47 |
50 virtual void Init() OVERRIDE { | 48 virtual void Init() OVERRIDE { |
51 service_ = DnsConfigService::CreateSystemService(); | 49 service_ = DnsConfigService::CreateSystemService(); |
52 service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig)); | 50 service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig)); |
53 } | 51 } |
54 | 52 |
55 virtual void CleanUp() OVERRIDE { | 53 virtual void CleanUp() OVERRIDE { service_.reset(); } |
56 service_.reset(); | |
57 } | |
58 | 54 |
59 private: | 55 private: |
60 scoped_ptr<DnsConfigService> service_; | 56 scoped_ptr<DnsConfigService> service_; |
61 | 57 |
62 DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread); | 58 DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread); |
63 }; | 59 }; |
64 | 60 |
65 NetworkChangeNotifierMac::NetworkChangeNotifierMac() | 61 NetworkChangeNotifierMac::NetworkChangeNotifierMac() |
66 : NetworkChangeNotifier(NetworkChangeCalculatorParamsMac()), | 62 : NetworkChangeNotifier(NetworkChangeCalculatorParamsMac()), |
67 connection_type_(CONNECTION_UNKNOWN), | 63 connection_type_(CONNECTION_UNKNOWN), |
68 connection_type_initialized_(false), | 64 connection_type_initialized_(false), |
69 initial_connection_type_cv_(&connection_type_lock_), | 65 initial_connection_type_cv_(&connection_type_lock_), |
70 forwarder_(this), | 66 forwarder_(this), |
71 dns_config_service_thread_(new DnsConfigServiceThread()) { | 67 dns_config_service_thread_(new DnsConfigServiceThread()) { |
72 // Must be initialized after the rest of this object, as it may call back into | 68 // Must be initialized after the rest of this object, as it may call back into |
73 // SetInitialConnectionType(). | 69 // SetInitialConnectionType(). |
74 config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_)); | 70 config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_)); |
75 dns_config_service_thread_->StartWithOptions( | 71 dns_config_service_thread_->StartWithOptions( |
76 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | 72 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
77 } | 73 } |
78 | 74 |
79 NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { | 75 NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { |
80 // Delete the ConfigWatcher to join the notifier thread, ensuring that | 76 // Delete the ConfigWatcher to join the notifier thread, ensuring that |
81 // StartReachabilityNotifications() has an opportunity to run to completion. | 77 // StartReachabilityNotifications() has an opportunity to run to completion. |
82 config_watcher_.reset(); | 78 config_watcher_.reset(); |
83 | 79 |
84 // Now that StartReachabilityNotifications() has either run to completion or | 80 // Now that StartReachabilityNotifications() has either run to completion or |
85 // never run at all, unschedule reachability_ if it was previously scheduled. | 81 // never run at all, unschedule reachability_ if it was previously scheduled. |
86 if (reachability_.get() && run_loop_.get()) { | 82 if (reachability_.get() && run_loop_.get()) { |
87 SCNetworkReachabilityUnscheduleFromRunLoop(reachability_.get(), | 83 SCNetworkReachabilityUnscheduleFromRunLoop( |
88 run_loop_.get(), | 84 reachability_.get(), run_loop_.get(), kCFRunLoopCommonModes); |
89 kCFRunLoopCommonModes); | |
90 } | 85 } |
91 } | 86 } |
92 | 87 |
93 // static | 88 // static |
94 NetworkChangeNotifier::NetworkChangeCalculatorParams | 89 NetworkChangeNotifier::NetworkChangeCalculatorParams |
95 NetworkChangeNotifierMac::NetworkChangeCalculatorParamsMac() { | 90 NetworkChangeNotifierMac::NetworkChangeCalculatorParamsMac() { |
96 NetworkChangeCalculatorParams params; | 91 NetworkChangeCalculatorParams params; |
97 // Delay values arrived at by simple experimentation and adjusted so as to | 92 // Delay values arrived at by simple experimentation and adjusted so as to |
98 // produce a single signal when switching between network connections. | 93 // produce a single signal when switching between network connections. |
99 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(500); | 94 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(500); |
100 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(500); | 95 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(500); |
101 params.connection_type_offline_delay_ = | 96 params.connection_type_offline_delay_ = |
102 base::TimeDelta::FromMilliseconds(1000); | 97 base::TimeDelta::FromMilliseconds(1000); |
103 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500); | 98 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500); |
104 return params; | 99 return params; |
105 } | 100 } |
106 | 101 |
107 NetworkChangeNotifier::ConnectionType | 102 NetworkChangeNotifier::ConnectionType |
108 NetworkChangeNotifierMac::GetCurrentConnectionType() const { | 103 NetworkChangeNotifierMac::GetCurrentConnectionType() const { |
109 base::AutoLock lock(connection_type_lock_); | 104 base::AutoLock lock(connection_type_lock_); |
110 // Make sure the initial connection type is set before returning. | 105 // Make sure the initial connection type is set before returning. |
111 while (!connection_type_initialized_) { | 106 while (!connection_type_initialized_) { |
112 initial_connection_type_cv_.Wait(); | 107 initial_connection_type_cv_.Wait(); |
113 } | 108 } |
114 return connection_type_; | 109 return connection_type_; |
115 } | 110 } |
116 | 111 |
117 void NetworkChangeNotifierMac::Forwarder::Init() { | 112 void NetworkChangeNotifierMac::Forwarder::Init() { |
118 net_config_watcher_->SetInitialConnectionType(); | 113 net_config_watcher_->SetInitialConnectionType(); |
119 } | 114 } |
120 | 115 |
121 void NetworkChangeNotifierMac::Forwarder::StartReachabilityNotifications() { | 116 void NetworkChangeNotifierMac::Forwarder::StartReachabilityNotifications() { |
122 net_config_watcher_->StartReachabilityNotifications(); | 117 net_config_watcher_->StartReachabilityNotifications(); |
123 } | 118 } |
124 | 119 |
125 void NetworkChangeNotifierMac::Forwarder::SetDynamicStoreNotificationKeys( | 120 void NetworkChangeNotifierMac::Forwarder::SetDynamicStoreNotificationKeys( |
126 SCDynamicStoreRef store) { | 121 SCDynamicStoreRef store) { |
127 net_config_watcher_->SetDynamicStoreNotificationKeys(store); | 122 net_config_watcher_->SetDynamicStoreNotificationKeys(store); |
128 } | 123 } |
129 | 124 |
130 void NetworkChangeNotifierMac::Forwarder::OnNetworkConfigChange( | 125 void NetworkChangeNotifierMac::Forwarder::OnNetworkConfigChange( |
131 CFArrayRef changed_keys) { | 126 CFArrayRef changed_keys) { |
132 net_config_watcher_->OnNetworkConfigChange(changed_keys); | 127 net_config_watcher_->OnNetworkConfigChange(changed_keys); |
133 } | 128 } |
134 | 129 |
135 void NetworkChangeNotifierMac::SetInitialConnectionType() { | 130 void NetworkChangeNotifierMac::SetInitialConnectionType() { |
136 // Called on notifier thread. | 131 // Called on notifier thread. |
137 | 132 |
138 // Try to reach 0.0.0.0. This is the approach taken by Firefox: | 133 // Try to reach 0.0.0.0. This is the approach taken by Firefox: |
139 // | 134 // |
140 // http://mxr.mozilla.org/mozilla2.0/source/netwerk/system/mac/nsNetworkLinkSe
rvice.mm | 135 // http://mxr.mozilla.org/mozilla2.0/source/netwerk/system/mac/nsNetworkLinkSe
rvice.mm |
141 // | 136 // |
(...skipping 21 matching lines...) Expand all Loading... |
163 } | 158 } |
164 } | 159 } |
165 | 160 |
166 void NetworkChangeNotifierMac::StartReachabilityNotifications() { | 161 void NetworkChangeNotifierMac::StartReachabilityNotifications() { |
167 // Called on notifier thread. | 162 // Called on notifier thread. |
168 run_loop_.reset(CFRunLoopGetCurrent()); | 163 run_loop_.reset(CFRunLoopGetCurrent()); |
169 CFRetain(run_loop_.get()); | 164 CFRetain(run_loop_.get()); |
170 | 165 |
171 DCHECK(reachability_); | 166 DCHECK(reachability_); |
172 SCNetworkReachabilityContext reachability_context = { | 167 SCNetworkReachabilityContext reachability_context = { |
173 0, // version | 168 0, // version |
174 this, // user data | 169 this, // user data |
175 NULL, // retain | 170 NULL, // retain |
176 NULL, // release | 171 NULL, // release |
177 NULL // description | 172 NULL // description |
178 }; | 173 }; |
179 if (!SCNetworkReachabilitySetCallback( | 174 if (!SCNetworkReachabilitySetCallback( |
180 reachability_, | 175 reachability_, |
181 &NetworkChangeNotifierMac::ReachabilityCallback, | 176 &NetworkChangeNotifierMac::ReachabilityCallback, |
182 &reachability_context)) { | 177 &reachability_context)) { |
183 LOG(DFATAL) << "Could not set network reachability callback"; | 178 LOG(DFATAL) << "Could not set network reachability callback"; |
184 reachability_.reset(); | 179 reachability_.reset(); |
185 } else if (!SCNetworkReachabilityScheduleWithRunLoop(reachability_, | 180 } else if (!SCNetworkReachabilityScheduleWithRunLoop( |
186 run_loop_, | 181 reachability_, run_loop_, kCFRunLoopCommonModes)) { |
187 kCFRunLoopCommonModes)) { | |
188 LOG(DFATAL) << "Could not schedule network reachability on run loop"; | 182 LOG(DFATAL) << "Could not schedule network reachability on run loop"; |
189 reachability_.reset(); | 183 reachability_.reset(); |
190 } | 184 } |
191 } | 185 } |
192 | 186 |
193 void NetworkChangeNotifierMac::SetDynamicStoreNotificationKeys( | 187 void NetworkChangeNotifierMac::SetDynamicStoreNotificationKeys( |
194 SCDynamicStoreRef store) { | 188 SCDynamicStoreRef store) { |
195 #if defined(OS_IOS) | 189 #if defined(OS_IOS) |
196 // SCDynamicStore API does not exist on iOS. | 190 // SCDynamicStore API does not exist on iOS. |
197 NOTREACHED(); | 191 NOTREACHED(); |
198 #else | 192 #else |
199 base::ScopedCFTypeRef<CFMutableArrayRef> notification_keys( | 193 base::ScopedCFTypeRef<CFMutableArrayRef> notification_keys( |
200 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); | 194 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); |
201 base::ScopedCFTypeRef<CFStringRef> key( | 195 base::ScopedCFTypeRef<CFStringRef> key( |
202 SCDynamicStoreKeyCreateNetworkGlobalEntity( | 196 SCDynamicStoreKeyCreateNetworkGlobalEntity( |
203 NULL, kSCDynamicStoreDomainState, kSCEntNetInterface)); | 197 NULL, kSCDynamicStoreDomainState, kSCEntNetInterface)); |
204 CFArrayAppendValue(notification_keys.get(), key.get()); | 198 CFArrayAppendValue(notification_keys.get(), key.get()); |
205 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( | 199 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( |
206 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)); | 200 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)); |
207 CFArrayAppendValue(notification_keys.get(), key.get()); | 201 CFArrayAppendValue(notification_keys.get(), key.get()); |
208 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( | 202 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( |
209 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)); | 203 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)); |
210 CFArrayAppendValue(notification_keys.get(), key.get()); | 204 CFArrayAppendValue(notification_keys.get(), key.get()); |
211 | 205 |
212 // Set the notification keys. This starts us receiving notifications. | 206 // Set the notification keys. This starts us receiving notifications. |
213 bool ret = SCDynamicStoreSetNotificationKeys( | 207 bool ret = |
214 store, notification_keys.get(), NULL); | 208 SCDynamicStoreSetNotificationKeys(store, notification_keys.get(), NULL); |
215 // TODO(willchan): Figure out a proper way to handle this rather than crash. | 209 // TODO(willchan): Figure out a proper way to handle this rather than crash. |
216 CHECK(ret); | 210 CHECK(ret); |
217 #endif // defined(OS_IOS) | 211 #endif // defined(OS_IOS) |
218 } | 212 } |
219 | 213 |
220 void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) { | 214 void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) { |
221 #if defined(OS_IOS) | 215 #if defined(OS_IOS) |
222 // SCDynamicStore API does not exist on iOS. | 216 // SCDynamicStore API does not exist on iOS. |
223 NOTREACHED(); | 217 NOTREACHED(); |
224 #else | 218 #else |
225 DCHECK_EQ(run_loop_.get(), CFRunLoopGetCurrent()); | 219 DCHECK_EQ(run_loop_.get(), CFRunLoopGetCurrent()); |
226 | 220 |
227 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) { | 221 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) { |
228 CFStringRef key = static_cast<CFStringRef>( | 222 CFStringRef key = |
229 CFArrayGetValueAtIndex(changed_keys, i)); | 223 static_cast<CFStringRef>(CFArrayGetValueAtIndex(changed_keys, i)); |
230 if (CFStringHasSuffix(key, kSCEntNetIPv4) || | 224 if (CFStringHasSuffix(key, kSCEntNetIPv4) || |
231 CFStringHasSuffix(key, kSCEntNetIPv6)) { | 225 CFStringHasSuffix(key, kSCEntNetIPv6)) { |
232 NotifyObserversOfIPAddressChange(); | 226 NotifyObserversOfIPAddressChange(); |
233 return; | 227 return; |
234 } | 228 } |
235 if (CFStringHasSuffix(key, kSCEntNetInterface)) { | 229 if (CFStringHasSuffix(key, kSCEntNetInterface)) { |
236 // TODO(willchan): Does not appear to be working. Look into this. | 230 // TODO(willchan): Does not appear to be working. Look into this. |
237 // Perhaps this isn't needed anyway. | 231 // Perhaps this isn't needed anyway. |
238 } else { | 232 } else { |
239 NOTREACHED(); | 233 NOTREACHED(); |
(...skipping 23 matching lines...) Expand all Loading... |
263 NotifyObserversOfConnectionTypeChange(); | 257 NotifyObserversOfConnectionTypeChange(); |
264 | 258 |
265 #if defined(OS_IOS) | 259 #if defined(OS_IOS) |
266 // On iOS, the SCDynamicStore API does not exist, and we use the reachability | 260 // On iOS, the SCDynamicStore API does not exist, and we use the reachability |
267 // API to detect IP address changes instead. | 261 // API to detect IP address changes instead. |
268 NotifyObserversOfIPAddressChange(); | 262 NotifyObserversOfIPAddressChange(); |
269 #endif // defined(OS_IOS) | 263 #endif // defined(OS_IOS) |
270 } | 264 } |
271 | 265 |
272 } // namespace net | 266 } // namespace net |
OLD | NEW |