| 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" |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 } | 33 } |
| 34 | 34 |
| 35 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() { | 35 NetworkChangeNotifierWin::~NetworkChangeNotifierWin() { |
| 36 if (is_watching_) { | 36 if (is_watching_) { |
| 37 CancelIPChangeNotify(&addr_overlapped_); | 37 CancelIPChangeNotify(&addr_overlapped_); |
| 38 addr_watcher_.StopWatching(); | 38 addr_watcher_.StopWatching(); |
| 39 } | 39 } |
| 40 WSACloseEvent(addr_overlapped_.hEvent); | 40 WSACloseEvent(addr_overlapped_.hEvent); |
| 41 } | 41 } |
| 42 | 42 |
| 43 // Conceptually we would like to tell whether the user is "online" verus | 43 // This implementation does not return the actual connection type but merely |
| 44 // "offline". This is challenging since the only thing we can test with | 44 // determines if the user is "online" (in which case it returns |
| 45 // certainty is whether a *particular* host is reachable. | 45 // CONNECTION_UNKNOWN) or "offline" (and then it returns CONNECTION_NONE). |
| 46 // This is challenging since the only thing we can test with certainty is |
| 47 // whether a *particular* host is reachable. |
| 46 // | 48 // |
| 47 // While we can't conclusively determine when a user is "online", we can at | 49 // While we can't conclusively determine when a user is "online", we can at |
| 48 // least reliably recognize some of the situtations when they are clearly | 50 // least reliably recognize some of the situtations when they are clearly |
| 49 // "offline". For example, if the user's laptop is not plugged into an ethernet | 51 // "offline". For example, if the user's laptop is not plugged into an ethernet |
| 50 // network and is not connected to any wireless networks, it must be offline. | 52 // network and is not connected to any wireless networks, it must be offline. |
| 51 // | 53 // |
| 52 // There are a number of different ways to implement this on Windows, each with | 54 // There are a number of different ways to implement this on Windows, each with |
| 53 // their pros and cons. Here is a comparison of various techniques considered: | 55 // their pros and cons. Here is a comparison of various techniques considered: |
| 54 // | 56 // |
| 55 // (1) Use InternetGetConnectedState (wininet.dll). This function is really easy | 57 // (1) Use InternetGetConnectedState (wininet.dll). This function is really easy |
| (...skipping 23 matching lines...) Expand all Loading... |
| 79 // workstation. Here is what I found: | 81 // workstation. Here is what I found: |
| 80 // * Approach (1) was pretty much zero-cost after the initial call. | 82 // * Approach (1) was pretty much zero-cost after the initial call. |
| 81 // * Approach (2) took an average of 3.25 milliseconds to enumerate the | 83 // * Approach (2) took an average of 3.25 milliseconds to enumerate the |
| 82 // adapters. | 84 // adapters. |
| 83 // * Approach (3) took an average of 0.8 ms to enumerate the providers. | 85 // * Approach (3) took an average of 0.8 ms to enumerate the providers. |
| 84 // | 86 // |
| 85 // In terms of correctness, all three approaches were comparable for the simple | 87 // In terms of correctness, all three approaches were comparable for the simple |
| 86 // experiments I ran... However none of them correctly returned "offline" when | 88 // experiments I ran... However none of them correctly returned "offline" when |
| 87 // executing 'ipconfig /release'. | 89 // executing 'ipconfig /release'. |
| 88 // | 90 // |
| 89 bool NetworkChangeNotifierWin::IsCurrentlyOffline() const { | 91 NetworkChangeNotifier::ConnectionType |
| 92 NetworkChangeNotifierWin::GetCurrentConnectionType() const { |
| 90 | 93 |
| 91 // TODO(eroman): We could cache this value, and only re-calculate it on | 94 // TODO(eroman): We could cache this value, and only re-calculate it on |
| 92 // network changes. For now we recompute it each time asked, | 95 // network changes. For now we recompute it each time asked, |
| 93 // since it is relatively fast (sub 1ms) and not called often. | 96 // since it is relatively fast (sub 1ms) and not called often. |
| 94 | 97 |
| 95 EnsureWinsockInit(); | 98 EnsureWinsockInit(); |
| 96 | 99 |
| 97 // The following code was adapted from: | 100 // The following code was adapted from: |
| 98 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/
base/win/async_network_alive_win32.cc?view=markup&pathrev=47343 | 101 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/net/notifier/
base/win/async_network_alive_win32.cc?view=markup&pathrev=47343 |
| 99 // The main difference is we only call WSALookupServiceNext once, whereas | 102 // The main difference is we only call WSALookupServiceNext once, whereas |
| 100 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS | 103 // the earlier code would traverse the entire list and pass LUP_FLUSHPREVIOUS |
| 101 // to skip past the large results. | 104 // to skip past the large results. |
| 102 | 105 |
| 103 HANDLE ws_handle; | 106 HANDLE ws_handle; |
| 104 WSAQUERYSET query_set = {0}; | 107 WSAQUERYSET query_set = {0}; |
| 105 query_set.dwSize = sizeof(WSAQUERYSET); | 108 query_set.dwSize = sizeof(WSAQUERYSET); |
| 106 query_set.dwNameSpace = NS_NLA; | 109 query_set.dwNameSpace = NS_NLA; |
| 107 // Initiate a client query to iterate through the | 110 // Initiate a client query to iterate through the |
| 108 // currently connected networks. | 111 // currently connected networks. |
| 109 if (0 != WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, | 112 if (0 != WSALookupServiceBegin(&query_set, LUP_RETURN_ALL, |
| 110 &ws_handle)) { | 113 &ws_handle)) { |
| 111 LOG(ERROR) << "WSALookupServiceBegin failed with: " << WSAGetLastError(); | 114 LOG(ERROR) << "WSALookupServiceBegin failed with: " << WSAGetLastError(); |
| 112 return false; | 115 return NetworkChangeNotifier::CONNECTION_UNKNOWN; |
| 113 } | 116 } |
| 114 | 117 |
| 115 bool found_connection = false; | 118 bool found_connection = false; |
| 116 | 119 |
| 117 // Retrieve the first available network. In this function, we only | 120 // Retrieve the first available network. In this function, we only |
| 118 // need to know whether or not there is network connection. | 121 // need to know whether or not there is network connection. |
| 119 // Allocate 256 bytes for name, it should be enough for most cases. | 122 // Allocate 256 bytes for name, it should be enough for most cases. |
| 120 // If the name is longer, it is OK as we will check the code returned and | 123 // If the name is longer, it is OK as we will check the code returned and |
| 121 // set correct network status. | 124 // set correct network status. |
| 122 char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; | 125 char result_buffer[sizeof(WSAQUERYSET) + 256] = {0}; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 147 // There was nothing to iterate over! | 150 // There was nothing to iterate over! |
| 148 } else { | 151 } else { |
| 149 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result; | 152 LOG(WARNING) << "WSALookupServiceNext() failed with:" << result; |
| 150 } | 153 } |
| 151 } | 154 } |
| 152 | 155 |
| 153 result = WSALookupServiceEnd(ws_handle); | 156 result = WSALookupServiceEnd(ws_handle); |
| 154 LOG_IF(ERROR, result != 0) | 157 LOG_IF(ERROR, result != 0) |
| 155 << "WSALookupServiceEnd() failed with: " << result; | 158 << "WSALookupServiceEnd() failed with: " << result; |
| 156 | 159 |
| 157 return !found_connection; | 160 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. |
| 161 return found_connection ? NetworkChangeNotifier::CONNECTION_UNKNOWN : |
| 162 NetworkChangeNotifier::CONNECTION_NONE; |
| 158 } | 163 } |
| 159 | 164 |
| 160 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) { | 165 void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) { |
| 161 DCHECK(CalledOnValidThread()); | 166 DCHECK(CalledOnValidThread()); |
| 162 DCHECK(is_watching_); | 167 DCHECK(is_watching_); |
| 163 is_watching_ = false; | 168 is_watching_ = false; |
| 164 | 169 |
| 165 // Start watching for the next address change. | 170 // Start watching for the next address change. |
| 166 WatchForAddressChange(); | 171 WatchForAddressChange(); |
| 167 | 172 |
| 168 NotifyObservers(); | 173 NotifyObservers(); |
| 169 } | 174 } |
| 170 | 175 |
| 171 void NetworkChangeNotifierWin::NotifyObservers() { | 176 void NetworkChangeNotifierWin::NotifyObservers() { |
| 172 DCHECK(CalledOnValidThread()); | 177 DCHECK(CalledOnValidThread()); |
| 173 NotifyObserversOfIPAddressChange(); | 178 NotifyObserversOfIPAddressChange(); |
| 174 | 179 |
| 175 // Calling IsOffline() at this very moment is likely to give | 180 // Calling GetConnectionType() at this very moment is likely to give |
| 176 // the wrong result, so we delay that until a little bit later. | 181 // the wrong result, so we delay that until a little bit later. |
| 177 // | 182 // |
| 178 // The one second delay chosen here was determined experimentally | 183 // The one second delay chosen here was determined experimentally |
| 179 // by adamk on Windows 7. | 184 // by adamk on Windows 7. |
| 180 timer_.Stop(); // cancel any already waiting notification | 185 timer_.Stop(); // cancel any already waiting notification |
| 181 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, | 186 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, |
| 182 &NetworkChangeNotifierWin::NotifyParentOfOnlineStateChange); | 187 &NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange); |
| 183 } | 188 } |
| 184 | 189 |
| 185 void NetworkChangeNotifierWin::WatchForAddressChange() { | 190 void NetworkChangeNotifierWin::WatchForAddressChange() { |
| 186 DCHECK(CalledOnValidThread()); | 191 DCHECK(CalledOnValidThread()); |
| 187 DCHECK(!is_watching_); | 192 DCHECK(!is_watching_); |
| 188 | 193 |
| 189 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown | 194 // NotifyAddrChange occasionally fails with ERROR_OPEN_FAILED for unknown |
| 190 // reasons. More rarely, it's also been observed failing with | 195 // reasons. More rarely, it's also been observed failing with |
| 191 // ERROR_NO_SYSTEM_RESOURCES. When either of these happens, we retry later. | 196 // ERROR_NO_SYSTEM_RESOURCES. When either of these happens, we retry later. |
| 192 if (!WatchForAddressChangeInternal()) { | 197 if (!WatchForAddressChangeInternal()) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 DCHECK(CalledOnValidThread()); | 233 DCHECK(CalledOnValidThread()); |
| 229 HANDLE handle = NULL; | 234 HANDLE handle = NULL; |
| 230 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_); | 235 DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_); |
| 231 if (ret != ERROR_IO_PENDING) | 236 if (ret != ERROR_IO_PENDING) |
| 232 return false; | 237 return false; |
| 233 | 238 |
| 234 addr_watcher_.StartWatching(addr_overlapped_.hEvent, this); | 239 addr_watcher_.StartWatching(addr_overlapped_.hEvent, this); |
| 235 return true; | 240 return true; |
| 236 } | 241 } |
| 237 | 242 |
| 238 void NetworkChangeNotifierWin::NotifyParentOfOnlineStateChange() { | 243 void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() { |
| 239 NotifyObserversOfOnlineStateChange(); | 244 NotifyObserversOfConnectionTypeChange(); |
| 240 } | 245 } |
| 241 | 246 |
| 242 } // namespace net | 247 } // namespace net |
| OLD | NEW |