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 "chrome/browser/chromeos/net/network_portal_detector.h" | 5 #include "chrome/browser/chromeos/net/network_portal_detector.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/message_loop.h" |
10 #include "chrome/browser/browser_process.h" | 11 #include "chrome/browser/browser_process.h" |
11 #include "chrome/browser/chromeos/cros/cros_library.h" | 12 #include "chrome/browser/chromeos/cros/cros_library.h" |
12 #include "chrome/common/chrome_switches.h" | 13 #include "chrome/common/chrome_switches.h" |
13 #include "content/public/browser/browser_thread.h" | 14 #include "content/public/browser/browser_thread.h" |
14 #include "grit/generated_resources.h" | 15 #include "grit/generated_resources.h" |
15 #include "ui/base/l10n/l10n_util.h" | 16 #include "ui/base/l10n/l10n_util.h" |
16 | 17 |
17 using captive_portal::CaptivePortalDetector; | 18 using captive_portal::CaptivePortalDetector; |
18 | 19 |
19 namespace chromeos { | 20 namespace chromeos { |
20 | 21 |
21 namespace { | 22 namespace { |
22 | 23 |
| 24 // Maximum number of portal detections for the same active network |
| 25 // after network change. |
| 26 const int kMaxRequestAttempts = 3; |
| 27 |
| 28 // Minimum timeout between consecutive portal checks for the same |
| 29 // network. |
| 30 const int kMinTimeBetweenAttemptsSec = 3; |
| 31 |
| 32 // Timeout for a portal check. |
| 33 const int kRequestTimeoutSec = 10; |
| 34 |
23 std::string CaptivePortalStateString( | 35 std::string CaptivePortalStateString( |
24 NetworkPortalDetector::CaptivePortalState state) { | 36 NetworkPortalDetector::CaptivePortalState state) { |
25 switch (state) { | 37 switch (state) { |
26 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_UNKNOWN: | 38 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_UNKNOWN: |
27 return l10n_util::GetStringUTF8( | 39 return l10n_util::GetStringUTF8( |
28 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_UNKNOWN); | 40 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_UNKNOWN); |
29 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_OFFLINE: | 41 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_OFFLINE: |
30 return l10n_util::GetStringUTF8( | 42 return l10n_util::GetStringUTF8( |
31 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_OFFLINE); | 43 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_OFFLINE); |
32 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_ONLINE: | 44 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_ONLINE: |
33 return l10n_util::GetStringUTF8(IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_ONLINE); | 45 return l10n_util::GetStringUTF8(IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_ONLINE); |
34 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_PORTAL: | 46 case NetworkPortalDetector::CAPTIVE_PORTAL_STATE_PORTAL: |
35 return l10n_util::GetStringUTF8(IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_PORTAL); | 47 return l10n_util::GetStringUTF8(IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_PORTAL); |
36 } | 48 } |
37 return l10n_util::GetStringUTF8( | 49 return l10n_util::GetStringUTF8( |
38 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_UNRECOGNIZED); | 50 IDS_CHROMEOS_CAPTIVE_PORTAL_STATE_UNRECOGNIZED); |
39 } | 51 } |
40 | 52 |
41 NetworkPortalDetector* g_network_portal_detector = NULL; | 53 NetworkPortalDetector* g_network_portal_detector = NULL; |
42 | 54 |
43 } // namespace | 55 } // namespace |
44 | 56 |
45 NetworkPortalDetector::NetworkPortalDetector( | 57 NetworkPortalDetector::NetworkPortalDetector( |
46 const scoped_refptr<net::URLRequestContextGetter>& request_context) | 58 const scoped_refptr<net::URLRequestContextGetter>& request_context) |
47 : test_url_(CaptivePortalDetector::kDefaultURL) { | 59 : test_url_(CaptivePortalDetector::kDefaultURL), |
| 60 weak_ptr_factory_(this), |
| 61 attempt_count_(0), |
| 62 min_time_between_attempts_( |
| 63 base::TimeDelta::FromSeconds(kMinTimeBetweenAttemptsSec)), |
| 64 request_timeout_(base::TimeDelta::FromSeconds(kRequestTimeoutSec)) { |
48 captive_portal_detector_.reset( | 65 captive_portal_detector_.reset( |
49 new CaptivePortalDetector(request_context)); | 66 new CaptivePortalDetector(request_context)); |
50 } | 67 } |
51 | 68 |
52 NetworkPortalDetector::~NetworkPortalDetector() { | 69 NetworkPortalDetector::~NetworkPortalDetector() { |
53 } | 70 } |
54 | 71 |
55 void NetworkPortalDetector::Init() { | 72 void NetworkPortalDetector::Init() { |
56 DCHECK(CalledOnValidThread()); | 73 DCHECK(CalledOnValidThread()); |
57 | 74 |
58 state_ = STATE_IDLE; | 75 state_ = STATE_IDLE; |
59 chromeos::NetworkLibrary* network_library = | 76 chromeos::NetworkLibrary* network_library = |
60 chromeos::CrosLibrary::Get()->GetNetworkLibrary(); | 77 chromeos::CrosLibrary::Get()->GetNetworkLibrary(); |
61 network_library->AddNetworkManagerObserver(this); | 78 network_library->AddNetworkManagerObserver(this); |
62 } | 79 } |
63 | 80 |
64 void NetworkPortalDetector::Shutdown() { | 81 void NetworkPortalDetector::Shutdown() { |
65 DCHECK(CalledOnValidThread()); | 82 DCHECK(CalledOnValidThread()); |
66 | 83 |
| 84 detection_task_.Cancel(); |
| 85 detection_timeout_.Cancel(); |
| 86 |
67 captive_portal_detector_->Cancel(); | 87 captive_portal_detector_->Cancel(); |
68 captive_portal_detector_.reset(); | 88 captive_portal_detector_.reset(); |
69 observers_.Clear(); | 89 observers_.Clear(); |
70 chromeos::NetworkLibrary* network_library = | 90 chromeos::NetworkLibrary* network_library = |
71 chromeos::CrosLibrary::Get()->GetNetworkLibrary(); | 91 chromeos::CrosLibrary::Get()->GetNetworkLibrary(); |
72 network_library->RemoveNetworkManagerObserver(this); | 92 network_library->RemoveNetworkManagerObserver(this); |
73 } | 93 } |
74 | 94 |
75 void NetworkPortalDetector::AddObserver(Observer* observer) { | 95 void NetworkPortalDetector::AddObserver(Observer* observer) { |
76 DCHECK(CalledOnValidThread()); | 96 DCHECK(CalledOnValidThread()); |
| 97 |
77 if (!observers_.HasObserver(observer)) | 98 if (!observers_.HasObserver(observer)) |
78 observers_.AddObserver(observer); | 99 observers_.AddObserver(observer); |
79 } | 100 } |
80 | 101 |
81 void NetworkPortalDetector::RemoveObserver(Observer* observer) { | 102 void NetworkPortalDetector::RemoveObserver(Observer* observer) { |
82 DCHECK(CalledOnValidThread()); | 103 DCHECK(CalledOnValidThread()); |
| 104 |
83 observers_.RemoveObserver(observer); | 105 observers_.RemoveObserver(observer); |
84 } | 106 } |
85 | 107 |
86 NetworkPortalDetector::CaptivePortalState | 108 NetworkPortalDetector::CaptivePortalState |
87 NetworkPortalDetector::GetCaptivePortalState(const Network* network) { | 109 NetworkPortalDetector::GetCaptivePortalState(const Network* network) { |
| 110 DCHECK(CalledOnValidThread()); |
| 111 |
88 if (!network) | 112 if (!network) |
89 return CAPTIVE_PORTAL_STATE_UNKNOWN; | 113 return CAPTIVE_PORTAL_STATE_UNKNOWN; |
90 CaptivePortalStateMap::const_iterator it = | 114 CaptivePortalStateMap::const_iterator it = |
91 captive_portal_state_map_.find(network->unique_id()); | 115 captive_portal_state_map_.find(network->unique_id()); |
92 if (it == captive_portal_state_map_.end()) | 116 if (it == captive_portal_state_map_.end()) |
93 return CAPTIVE_PORTAL_STATE_UNKNOWN; | 117 return CAPTIVE_PORTAL_STATE_UNKNOWN; |
94 return it->second; | 118 return it->second; |
95 } | 119 } |
96 | 120 |
97 void NetworkPortalDetector::OnNetworkManagerChanged(NetworkLibrary* cros) { | 121 void NetworkPortalDetector::OnNetworkManagerChanged(NetworkLibrary* cros) { |
98 DCHECK(CalledOnValidThread()); | 122 DCHECK(CalledOnValidThread()); |
99 | 123 |
100 // Suppose that if there are no active network, then unique id is | 124 // Suppose that if there are no active network, then unique id is |
101 // empty and connection state is unknown. | 125 // empty and connection state is unknown. |
102 std::string new_active_network_id; | 126 std::string new_active_network_id; |
103 ConnectionState connection_state = STATE_UNKNOWN; | 127 ConnectionState connection_state = STATE_UNKNOWN; |
104 const Network* active_network = cros->active_network(); | 128 const Network* active_network = cros->active_network(); |
105 if (active_network) { | 129 if (active_network) { |
106 new_active_network_id = active_network->unique_id(); | 130 new_active_network_id = active_network->unique_id(); |
107 connection_state = active_network->connection_state(); | 131 connection_state = active_network->connection_state(); |
108 } | 132 } |
109 | 133 |
110 if (active_network_id_ != new_active_network_id) { | 134 if (active_network_id_ != new_active_network_id) { |
111 active_network_id_ = new_active_network_id; | 135 active_network_id_ = new_active_network_id; |
112 if (IsCheckingForPortal()) { | 136 attempt_count_ = 0; |
113 // Network is changed, so detection results will be incorrect. | 137 if (IsPortalCheckPending()) { |
114 state_ = STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED; | 138 detection_task_.Cancel(); |
115 } else if (Network::IsConnectedState(connection_state)) { | 139 detection_timeout_.Cancel(); |
116 // Start captive portal detection, if possible. | 140 } else if (IsCheckingForPortal()) { |
117 DetectCaptivePortal(); | 141 captive_portal_detector_->Cancel(); |
118 } | 142 } |
119 } else if (!IsCheckingForPortal() && | 143 state_ = STATE_IDLE; |
120 Network::IsConnectedState(connection_state)) { | 144 } |
| 145 if (!IsCheckingForPortal() && !IsPortalCheckPending() && |
| 146 Network::IsConnectedState(connection_state) && |
| 147 attempt_count_ < kMaxRequestAttempts) { |
121 DCHECK(active_network); | 148 DCHECK(active_network); |
| 149 |
122 // Initiate Captive Portal detection only if network's captive | 150 // Initiate Captive Portal detection only if network's captive |
123 // portal state is unknown (e.g. for freshly created networks) or offline. | 151 // portal state is unknown (e.g. for freshly created networks) or |
| 152 // offline. |
124 CaptivePortalState state = GetCaptivePortalState(active_network); | 153 CaptivePortalState state = GetCaptivePortalState(active_network); |
125 if (state == CAPTIVE_PORTAL_STATE_UNKNOWN || | 154 if (state == CAPTIVE_PORTAL_STATE_UNKNOWN || |
126 state == CAPTIVE_PORTAL_STATE_OFFLINE) { | 155 state == CAPTIVE_PORTAL_STATE_OFFLINE) { |
127 DetectCaptivePortal(); | 156 DetectCaptivePortal(base::TimeDelta()); |
128 } | 157 } |
129 } | 158 } |
130 } | 159 } |
131 | 160 |
132 // static | 161 // static |
133 NetworkPortalDetector* NetworkPortalDetector::CreateInstance() { | 162 NetworkPortalDetector* NetworkPortalDetector::CreateInstance() { |
134 DCHECK(!g_network_portal_detector); | 163 DCHECK(!g_network_portal_detector); |
135 g_network_portal_detector = new NetworkPortalDetector( | 164 g_network_portal_detector = new NetworkPortalDetector( |
136 g_browser_process->system_request_context()); | 165 g_browser_process->system_request_context()); |
137 return g_network_portal_detector; | 166 return g_network_portal_detector; |
138 } | 167 } |
139 | 168 |
140 // static | 169 // static |
141 NetworkPortalDetector* NetworkPortalDetector::GetInstance() { | 170 NetworkPortalDetector* NetworkPortalDetector::GetInstance() { |
142 if (!g_network_portal_detector) | 171 if (!g_network_portal_detector) |
143 return CreateInstance(); | 172 return CreateInstance(); |
144 return g_network_portal_detector; | 173 return g_network_portal_detector; |
145 } | 174 } |
146 | 175 |
147 // static | 176 // static |
148 bool NetworkPortalDetector::IsEnabled() { | 177 bool NetworkPortalDetector::IsEnabled() { |
149 return !CommandLine::ForCurrentProcess()->HasSwitch( | 178 return !CommandLine::ForCurrentProcess()->HasSwitch( |
150 switches::kDisableChromeCaptivePortalDetector); | 179 switches::kDisableChromeCaptivePortalDetector); |
151 } | 180 } |
152 | 181 |
153 void NetworkPortalDetector::DetectCaptivePortal() { | 182 void NetworkPortalDetector::DetectCaptivePortal(const base::TimeDelta& delay) { |
| 183 DCHECK(!IsPortalCheckPending()); |
| 184 DCHECK(!IsCheckingForPortal()); |
| 185 DCHECK(attempt_count_ < kMaxRequestAttempts); |
| 186 |
| 187 detection_task_.Cancel(); |
| 188 detection_timeout_.Cancel(); |
| 189 state_ = STATE_PORTAL_CHECK_PENDING; |
| 190 |
| 191 next_attempt_delay_ = delay; |
| 192 if (attempt_count_ > 0) { |
| 193 base::TimeTicks now = GetCurrentTimeTicks(); |
| 194 base::TimeDelta elapsed_time; |
| 195 if (now > attempt_start_time_) |
| 196 elapsed_time = now - attempt_start_time_; |
| 197 if (elapsed_time < min_time_between_attempts_ && |
| 198 min_time_between_attempts_ - elapsed_time > next_attempt_delay_) { |
| 199 next_attempt_delay_ = min_time_between_attempts_ - elapsed_time; |
| 200 } |
| 201 } |
| 202 detection_task_.Reset( |
| 203 base::Bind(&NetworkPortalDetector::DetectCaptivePortalTask, |
| 204 weak_ptr_factory_.GetWeakPtr())); |
| 205 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 206 detection_task_.callback(), |
| 207 next_attempt_delay_); |
| 208 } |
| 209 |
| 210 void NetworkPortalDetector::DetectCaptivePortalTask() { |
| 211 DCHECK(IsPortalCheckPending()); |
| 212 |
154 state_ = STATE_CHECKING_FOR_PORTAL; | 213 state_ = STATE_CHECKING_FOR_PORTAL; |
| 214 |
| 215 ++attempt_count_; |
| 216 attempt_start_time_ = GetCurrentTimeTicks(); |
| 217 |
| 218 VLOG(1) << "Portal detection started: " |
| 219 << "network=" << active_network_id_ << ", " |
| 220 << "attempt=" << attempt_count_ << " of " << kMaxRequestAttempts; |
| 221 |
155 captive_portal_detector_->DetectCaptivePortal( | 222 captive_portal_detector_->DetectCaptivePortal( |
156 test_url_, | 223 test_url_, |
157 base::Bind(&NetworkPortalDetector::OnPortalDetectionCompleted, | 224 base::Bind(&NetworkPortalDetector::OnPortalDetectionCompleted, |
158 base::Unretained(this))); | 225 weak_ptr_factory_.GetWeakPtr())); |
| 226 detection_timeout_.Reset( |
| 227 base::Bind(&NetworkPortalDetector::PortalDetectionTimeout, |
| 228 weak_ptr_factory_.GetWeakPtr())); |
| 229 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 230 detection_timeout_.callback(), |
| 231 request_timeout_); |
| 232 } |
| 233 |
| 234 void NetworkPortalDetector::PortalDetectionTimeout() { |
| 235 DCHECK(CalledOnValidThread()); |
| 236 DCHECK(IsCheckingForPortal()); |
| 237 |
| 238 VLOG(1) << "Portal detection timeout: network=" << active_network_id_; |
| 239 |
| 240 captive_portal_detector_->Cancel(); |
| 241 CaptivePortalDetector::Results results; |
| 242 results.result = captive_portal::RESULT_NO_RESPONSE; |
| 243 OnPortalDetectionCompleted(results); |
159 } | 244 } |
160 | 245 |
161 void NetworkPortalDetector::OnPortalDetectionCompleted( | 246 void NetworkPortalDetector::OnPortalDetectionCompleted( |
162 const CaptivePortalDetector::Results& results) { | 247 const CaptivePortalDetector::Results& results) { |
163 DCHECK(CalledOnValidThread()); | 248 DCHECK(CalledOnValidThread()); |
| 249 DCHECK(IsCheckingForPortal()); |
164 | 250 |
165 DCHECK(IsCheckingForPortal()); | 251 VLOG(1) << "Portal detection completed: " |
| 252 << "network=" << active_network_id_ << ", " |
| 253 << "result=" << CaptivePortalDetector::CaptivePortalResultToString( |
| 254 results.result); |
| 255 |
| 256 state_ = STATE_IDLE; |
| 257 detection_timeout_.Cancel(); |
166 | 258 |
167 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); | 259 NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); |
168 const Network* active_network = cros->active_network(); | 260 const Network* active_network = cros->active_network(); |
169 if (!active_network) | 261 if (!active_network) |
170 return; | 262 return; |
171 | 263 |
172 if (state_ == STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED) { | |
173 // Can't use detection results, because network was changed. So | |
174 // retry portal detection for the active network or, if possible. | |
175 if (Network::IsConnectedState(active_network->connection_state()) && | |
176 GetCaptivePortalState(active_network) == | |
177 CAPTIVE_PORTAL_STATE_UNKNOWN) { | |
178 DetectCaptivePortal(); | |
179 } | |
180 return; | |
181 } | |
182 | |
183 state_ = STATE_IDLE; | |
184 | |
185 switch (results.result) { | 264 switch (results.result) { |
186 case captive_portal::RESULT_NO_RESPONSE: | 265 case captive_portal::RESULT_NO_RESPONSE: |
187 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_OFFLINE); | 266 if (attempt_count_ >= kMaxRequestAttempts) |
| 267 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_OFFLINE); |
| 268 else |
| 269 DetectCaptivePortal(results.retry_after_delta); |
188 break; | 270 break; |
189 case captive_portal::RESULT_INTERNET_CONNECTED: | 271 case captive_portal::RESULT_INTERNET_CONNECTED: |
190 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_ONLINE); | 272 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_ONLINE); |
191 break; | 273 break; |
192 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: | 274 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: |
193 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_PORTAL); | 275 SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_PORTAL); |
194 break; | 276 break; |
195 default: | 277 default: |
196 break; | 278 break; |
197 } | 279 } |
198 } | 280 } |
199 | 281 |
| 282 bool NetworkPortalDetector::IsPortalCheckPending() const { |
| 283 return state_ == STATE_PORTAL_CHECK_PENDING; |
| 284 } |
| 285 |
200 bool NetworkPortalDetector::IsCheckingForPortal() const { | 286 bool NetworkPortalDetector::IsCheckingForPortal() const { |
201 return state_ == STATE_CHECKING_FOR_PORTAL || | 287 return state_ == STATE_CHECKING_FOR_PORTAL; |
202 state_ == STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED; | |
203 } | 288 } |
204 | 289 |
205 void NetworkPortalDetector::SetCaptivePortalState(const Network* network, | 290 void NetworkPortalDetector::SetCaptivePortalState(const Network* network, |
206 CaptivePortalState state) { | 291 CaptivePortalState state) { |
207 DCHECK(network); | 292 DCHECK(network); |
| 293 |
208 CaptivePortalStateMap::const_iterator it = | 294 CaptivePortalStateMap::const_iterator it = |
209 captive_portal_state_map_.find(network->unique_id()); | 295 captive_portal_state_map_.find(network->unique_id()); |
210 if (it == captive_portal_state_map_.end() || | 296 if (it == captive_portal_state_map_.end() || |
211 it->second != state) { | 297 it->second != state) { |
212 VLOG(2) << "Updating Chrome Captive Portal state: " | 298 VLOG(1) << "Updating Chrome Captive Portal state: " |
213 << "network=" << network->unique_id() << ", " | 299 << "network=" << network->unique_id() << ", " |
214 << "state=" << CaptivePortalStateString(state); | 300 << "state=" << CaptivePortalStateString(state); |
215 captive_portal_state_map_[network->unique_id()] = state; | 301 captive_portal_state_map_[network->unique_id()] = state; |
216 NotifyPortalStateChanged(network, state); | 302 NotifyPortalStateChanged(network, state); |
217 } | 303 } |
218 } | 304 } |
219 | 305 |
220 void NetworkPortalDetector::NotifyPortalStateChanged(const Network* network, | 306 void NetworkPortalDetector::NotifyPortalStateChanged(const Network* network, |
221 CaptivePortalState state) { | 307 CaptivePortalState state) { |
222 FOR_EACH_OBSERVER(Observer, observers_, OnPortalStateChanged(network, state)); | 308 FOR_EACH_OBSERVER(Observer, observers_, OnPortalStateChanged(network, state)); |
223 } | 309 } |
224 | 310 |
| 311 base::TimeTicks NetworkPortalDetector::GetCurrentTimeTicks() const { |
| 312 if (time_ticks_for_testing_.is_null()) |
| 313 return base::TimeTicks::Now(); |
| 314 else |
| 315 return time_ticks_for_testing_; |
| 316 } |
| 317 |
225 } // namespace chromeos | 318 } // namespace chromeos |
OLD | NEW |