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_win.h" | 5 #include "net/base/network_change_notifier_win.h" |
6 | 6 |
7 #include <iphlpapi.h> | 7 #include <iphlpapi.h> |
8 #include <winsock2.h> | 8 #include <winsock2.h> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/macros.h" | 13 #include "base/macros.h" |
14 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
16 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
17 #include "base/task_runner_util.h" | |
17 #include "base/threading/thread.h" | 18 #include "base/threading/thread.h" |
18 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
19 #include "base/time/time.h" | 20 #include "base/time/time.h" |
20 #include "net/base/winsock_init.h" | 21 #include "net/base/winsock_init.h" |
21 #include "net/base/winsock_util.h" | 22 #include "net/base/winsock_util.h" |
22 #include "net/dns/dns_config_service.h" | 23 #include "net/dns/dns_config_service.h" |
23 | 24 |
24 namespace net { | 25 namespace net { |
25 | 26 |
26 namespace { | 27 namespace { |
(...skipping 25 matching lines...) Expand all Loading... | |
52 }; | 53 }; |
53 | 54 |
54 NetworkChangeNotifierWin::NetworkChangeNotifierWin() | 55 NetworkChangeNotifierWin::NetworkChangeNotifierWin() |
55 : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()), | 56 : NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()), |
56 is_watching_(false), | 57 is_watching_(false), |
57 sequential_failures_(0), | 58 sequential_failures_(0), |
58 dns_config_service_thread_(new DnsConfigServiceThread()), | 59 dns_config_service_thread_(new DnsConfigServiceThread()), |
59 last_computed_connection_type_(RecomputeCurrentConnectionType()), | 60 last_computed_connection_type_(RecomputeCurrentConnectionType()), |
60 last_announced_offline_(last_computed_connection_type_ == | 61 last_announced_offline_(last_computed_connection_type_ == |
61 CONNECTION_NONE), | 62 CONNECTION_NONE), |
63 calculate_connection_type_(base::BindRepeating( | |
64 &NetworkChangeNotifierWin::RecomputeCurrentConnectionType)), | |
62 weak_factory_(this) { | 65 weak_factory_(this) { |
63 memset(&addr_overlapped_, 0, sizeof addr_overlapped_); | 66 memset(&addr_overlapped_, 0, sizeof addr_overlapped_); |
64 addr_overlapped_.hEvent = WSACreateEvent(); | 67 addr_overlapped_.hEvent = WSACreateEvent(); |
65 } | 68 } |
66 | 69 |
67 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() { | 70 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() { |
68 if (is_watching_) { | 71 if (is_watching_) { |
69 CancelIPChangeNotify(&addr_overlapped_); | 72 CancelIPChangeNotify(&addr_overlapped_); |
70 addr_watcher_.StopWatching(); | 73 addr_watcher_.StopWatching(); |
71 } | 74 } |
72 WSACloseEvent(addr_overlapped_.hEvent); | 75 WSACloseEvent(addr_overlapped_.hEvent); |
73 } | 76 } |
74 | 77 |
75 // static | 78 // static |
76 NetworkChangeNotifier::NetworkChangeCalculatorParams | 79 NetworkChangeNotifier::NetworkChangeCalculatorParams |
77 NetworkChangeNotifierWin::NetworkChangeCalculatorParamsWin() { | 80 NetworkChangeNotifierWin::NetworkChangeCalculatorParamsWin() { |
78 NetworkChangeCalculatorParams params; | 81 NetworkChangeCalculatorParams params; |
79 // Delay values arrived at by simple experimentation and adjusted so as to | 82 // Delay values arrived at by simple experimentation and adjusted so as to |
80 // produce a single signal when switching between network connections. | 83 // produce a single signal when switching between network connections. |
81 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(1500); | 84 params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(1500); |
82 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1500); | 85 params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1500); |
83 params.connection_type_offline_delay_ = | 86 params.connection_type_offline_delay_ = |
84 base::TimeDelta::FromMilliseconds(1500); | 87 base::TimeDelta::FromMilliseconds(1500); |
85 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500); | 88 params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500); |
86 return params; | 89 return params; |
87 } | 90 } |
88 | 91 |
92 NetworkChangeNotifier::ConnectionType | |
93 NetworkChangeNotifierWin::GetCurrentConnectionType() const { | |
94 base::AutoLock auto_lock(last_computed_connection_type_lock_); | |
95 return last_computed_connection_type_; | |
96 } | |
pauljensen
2017/05/23 14:55:51
can we move this back down?
jkarlin
2017/05/23 15:11:06
Done.
| |
97 | |
89 // This implementation does not return the actual connection type but merely | 98 // This implementation does not return the actual connection type but merely |
90 // determines if the user is "online" (in which case it returns | 99 // determines if the user is "online" (in which case it returns |
91 // CONNECTION_UNKNOWN) or "offline" (and then it returns CONNECTION_NONE). | 100 // CONNECTION_UNKNOWN) or "offline" (and then it returns CONNECTION_NONE). |
92 // This is challenging since the only thing we can test with certainty is | 101 // This is challenging since the only thing we can test with certainty is |
93 // whether a *particular* host is reachable. | 102 // whether a *particular* host is reachable. |
94 // | 103 // |
95 // While we can't conclusively determine when a user is "online", we can at | 104 // While we can't conclusively determine when a user is "online", we can at |
96 // least reliably recognize some of the situtations when they are clearly | 105 // least reliably recognize some of the situtations when they are clearly |
97 // "offline". For example, if the user's laptop is not plugged into an ethernet | 106 // "offline". For example, if the user's laptop is not plugged into an ethernet |
98 // network and is not connected to any wireless networks, it must be offline. | 107 // network and is not connected to any wireless networks, it must be offline. |
(...skipping 28 matching lines...) Expand all Loading... | |
127 // workstation. Here is what I found: | 136 // workstation. Here is what I found: |
128 // * Approach (1) was pretty much zero-cost after the initial call. | 137 // * Approach (1) was pretty much zero-cost after the initial call. |
129 // * Approach (2) took an average of 3.25 milliseconds to enumerate the | 138 // * Approach (2) took an average of 3.25 milliseconds to enumerate the |
130 // adapters. | 139 // adapters. |
131 // * Approach (3) took an average of 0.8 ms to enumerate the providers. | 140 // * Approach (3) took an average of 0.8 ms to enumerate the providers. |
132 // | 141 // |
133 // In terms of correctness, all three approaches were comparable for the simple | 142 // In terms of correctness, all three approaches were comparable for the simple |
134 // experiments I ran... However none of them correctly returned "offline" when | 143 // experiments I ran... However none of them correctly returned "offline" when |
135 // executing 'ipconfig /release'. | 144 // executing 'ipconfig /release'. |
136 // | 145 // |
146 // static | |
137 NetworkChangeNotifier::ConnectionType | 147 NetworkChangeNotifier::ConnectionType |
138 NetworkChangeNotifierWin::RecomputeCurrentConnectionType() const { | 148 NetworkChangeNotifierWin::RecomputeCurrentConnectionType() { |
139 DCHECK(CalledOnValidThread()); | |
140 | |
141 EnsureWinsockInit(); | 149 EnsureWinsockInit(); |
142 | 150 |
143 // The following code was adapted from: | 151 // The following code was adapted from: |
144 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/ base/win/async_network_alive_win32.cc?view=markup&pathrev=47343 | 152 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/ base/win/async_network_alive_win32.cc?view=markup&pathrev=47343 |
145 // The main difference is we only call WSALookupServiceNext once, whereas | 153 // The main difference is we only call WSALookupServiceNext once, whereas |
146 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS | 154 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS |
147 // to skip past the large results. | 155 // to skip past the large results. |
148 | 156 |
149 HANDLE ws_handle; | 157 HANDLE ws_handle; |
150 WSAQUERYSET query_set = {0}; | 158 WSAQUERYSET query_set = {0}; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
194 } else { | 202 } else { |
195 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result; | 203 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result; |
196 } | 204 } |
197 } | 205 } |
198 | 206 |
199 result = WSALookupServiceEnd(ws_handle); | 207 result = WSALookupServiceEnd(ws_handle); |
200 LOG_IF(ERROR, result != 0) | 208 LOG_IF(ERROR, result != 0) |
201 << "WSALookupServiceEnd() failed with: " << result; | 209 << "WSALookupServiceEnd() failed with: " << result; |
202 | 210 |
203 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. | 211 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. |
204 return found_connection ? ConnectionTypeFromInterfaces() | 212 return found_connection ? ConnectionTypeFromInterfaces() : CONNECTION_NONE; |
205 : NetworkChangeNotifier::CONNECTION_NONE; | |
206 } | |
207 | |
208 NetworkChangeNotifier::ConnectionType | |
209 NetworkChangeNotifierWin::GetCurrentConnectionType() const { | |
210 base::AutoLock auto_lock(last_computed_connection_type_lock_); | |
211 return last_computed_connection_type_; | |
212 } | 213 } |
213 | 214 |
214 void NetworkChangeNotifierWin::SetCurrentConnectionType( | 215 void NetworkChangeNotifierWin::SetCurrentConnectionType( |
215 ConnectionType connection_type) { | 216 ConnectionType connection_type) { |
216 base::AutoLock auto_lock(last_computed_connection_type_lock_); | 217 base::AutoLock auto_lock(last_computed_connection_type_lock_); |
217 last_computed_connection_type_ = connection_type; | 218 last_computed_connection_type_ = connection_type; |
218 } | 219 } |
219 | 220 |
220 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) { | 221 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) { |
221 DCHECK(CalledOnValidThread()); | 222 DCHECK(CalledOnValidThread()); |
222 DCHECK(is_watching_); | 223 DCHECK(is_watching_); |
223 is_watching_ = false; | 224 is_watching_ = false; |
224 | 225 |
225 // Start watching for the next address change. | 226 // Start watching for the next address change. |
226 WatchForAddressChange(); | 227 WatchForAddressChange(); |
228 DCHECK(dns_task_runner_); | |
227 | 229 |
228 NotifyObservers(); | 230 base::PostTaskAndReplyWithResult( |
231 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_, | |
232 base::Bind(&NetworkChangeNotifierWin::NotifyObservers, | |
233 weak_factory_.GetWeakPtr())); | |
229 } | 234 } |
230 | 235 |
231 void NetworkChangeNotifierWin::NotifyObservers() { | 236 void NetworkChangeNotifierWin::NotifyObservers(ConnectionType connection_type) { |
232 DCHECK(CalledOnValidThread()); | 237 DCHECK(CalledOnValidThread()); |
233 SetCurrentConnectionType(RecomputeCurrentConnectionType()); | 238 SetCurrentConnectionType(connection_type); |
234 NotifyObserversOfIPAddressChange(); | 239 NotifyObserversOfIPAddressChange(); |
235 | 240 |
236 // Calling GetConnectionType() at this very moment is likely to give | 241 // Calling GetConnectionType() at this very moment is likely to give |
237 // the wrong result, so we delay that until a little bit later. | 242 // the wrong result, so we delay that until a little bit later. |
238 // | 243 // |
239 // The one second delay chosen here was determined experimentally | 244 // The one second delay chosen here was determined experimentally |
240 // by adamk on Windows 7. | 245 // by adamk on Windows 7. |
241 // If after one second we determine we are still offline, we will | 246 // If after one second we determine we are still offline, we will |
242 // delay again. | 247 // delay again. |
243 offline_polls_ = 0; | 248 offline_polls_ = 0; |
249 // TODO(jkarlin): I'm not sure that calling with 'this' is safe. Can it be | |
250 // deleted? | |
244 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, | 251 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, |
245 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); | 252 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); |
246 } | 253 } |
247 | 254 |
248 void NetworkChangeNotifierWin::WatchForAddressChange() { | 255 void NetworkChangeNotifierWin::WatchForAddressChange() { |
249 DCHECK(CalledOnValidThread()); | 256 DCHECK(CalledOnValidThread()); |
250 DCHECK(!is_watching_); | 257 DCHECK(!is_watching_); |
251 | 258 |
252 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown | 259 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown |
253 // reasons. More rarely, it's also been observed failing with | 260 // reasons. More rarely, it's also been observed failing with |
(...skipping 13 matching lines...) Expand all Loading... | |
267 FROM_HERE, base::Bind(&NetworkChangeNotifierWin::WatchForAddressChange, | 274 FROM_HERE, base::Bind(&NetworkChangeNotifierWin::WatchForAddressChange, |
268 weak_factory_.GetWeakPtr()), | 275 weak_factory_.GetWeakPtr()), |
269 base::TimeDelta::FromMilliseconds( | 276 base::TimeDelta::FromMilliseconds( |
270 kWatchForAddressChangeRetryIntervalMs)); | 277 kWatchForAddressChangeRetryIntervalMs)); |
271 return; | 278 return; |
272 } | 279 } |
273 | 280 |
274 // Treat the transition from NotifyAddrChange failing to succeeding as a | 281 // Treat the transition from NotifyAddrChange failing to succeeding as a |
275 // network change event, since network changes were not being observed in | 282 // network change event, since network changes were not being observed in |
276 // that interval. | 283 // that interval. |
277 if (sequential_failures_ > 0) | 284 if (sequential_failures_ > 0) { |
278 NotifyObservers(); | 285 base::PostTaskAndReplyWithResult( |
286 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_, | |
287 base::Bind(&NetworkChangeNotifierWin::NotifyObservers, | |
288 weak_factory_.GetWeakPtr())); | |
289 } | |
279 | 290 |
280 if (sequential_failures_ < 2000) { | 291 if (sequential_failures_ < 2000) { |
281 UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures", | 292 UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures", |
282 sequential_failures_); | 293 sequential_failures_); |
283 } | 294 } |
284 | 295 |
285 is_watching_ = true; | 296 is_watching_ = true; |
286 sequential_failures_ = 0; | 297 sequential_failures_ = 0; |
287 } | 298 } |
288 | 299 |
289 bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() { | 300 bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() { |
290 DCHECK(CalledOnValidThread()); | 301 DCHECK(CalledOnValidThread()); |
291 | 302 |
292 if (!dns_config_service_thread_->IsRunning()) { | 303 if (!dns_config_service_thread_->IsRunning()) { |
293 dns_config_service_thread_->StartWithOptions( | 304 dns_config_service_thread_->StartWithOptions( |
294 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | 305 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
306 if (!dns_task_runner_) | |
307 dns_task_runner_ = | |
308 dns_config_service_thread_->message_loop()->task_runner(); | |
295 } | 309 } |
296 | 310 |
297 ResetEventIfSignaled(addr_overlapped_.hEvent); | 311 ResetEventIfSignaled(addr_overlapped_.hEvent); |
298 HANDLE handle = NULL; | 312 HANDLE handle = NULL; |
299 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_); | 313 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_); |
300 if (ret != ERROR_IO_PENDING) | 314 if (ret != ERROR_IO_PENDING) |
301 return false; | 315 return false; |
302 | 316 |
303 addr_watcher_.StartWatchingOnce(addr_overlapped_.hEvent, this); | 317 addr_watcher_.StartWatchingOnce(addr_overlapped_.hEvent, this); |
304 return true; | 318 return true; |
305 } | 319 } |
306 | 320 |
307 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() { | 321 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() { |
308 SetCurrentConnectionType(RecomputeCurrentConnectionType()); | 322 base::PostTaskAndReplyWithResult( |
323 dns_task_runner_.get(), FROM_HERE, calculate_connection_type_, | |
324 base::Bind( | |
325 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl, | |
326 weak_factory_.GetWeakPtr())); | |
327 } | |
328 | |
329 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChangeImpl( | |
330 ConnectionType connection_type) { | |
331 SetCurrentConnectionType(connection_type); | |
309 bool current_offline = IsOffline(); | 332 bool current_offline = IsOffline(); |
310 offline_polls_++; | 333 offline_polls_++; |
311 // If we continue to appear offline, delay sending out the notification in | 334 // If we continue to appear offline, delay sending out the notification in |
312 // case we appear to go online within 20 seconds. UMA histogram data shows | 335 // case we appear to go online within 20 seconds. UMA histogram data shows |
313 // we may not detect the transition to online state after 1 second but within | 336 // we may not detect the transition to online state after 1 second but within |
314 // 20 seconds we generally do. | 337 // 20 seconds we generally do. |
315 if (last_announced_offline_ && current_offline && offline_polls_ <= 20) { | 338 if (last_announced_offline_ && current_offline && offline_polls_ <= 20) { |
316 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, | 339 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, |
317 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); | 340 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); |
318 return; | 341 return; |
319 } | 342 } |
320 if (last_announced_offline_) | 343 if (last_announced_offline_) |
321 UMA_HISTOGRAM_CUSTOM_COUNTS("NCN.OfflinePolls", offline_polls_, 1, 50, 50); | 344 UMA_HISTOGRAM_CUSTOM_COUNTS("NCN.OfflinePolls", offline_polls_, 1, 50, 50); |
322 last_announced_offline_ = current_offline; | 345 last_announced_offline_ = current_offline; |
323 | 346 |
324 NotifyObserversOfConnectionTypeChange(); | 347 NotifyObserversOfConnectionTypeChange(); |
325 double max_bandwidth_mbps = 0.0; | 348 double max_bandwidth_mbps = 0.0; |
326 ConnectionType connection_type = CONNECTION_NONE; | 349 ConnectionType max_connection_type = CONNECTION_NONE; |
327 GetCurrentMaxBandwidthAndConnectionType(&max_bandwidth_mbps, | 350 GetCurrentMaxBandwidthAndConnectionType(&max_bandwidth_mbps, |
328 &connection_type); | 351 &max_connection_type); |
329 NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, connection_type); | 352 NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps, max_connection_type); |
330 } | 353 } |
331 | 354 |
332 } // namespace net | 355 } // namespace net |
OLD | NEW |