Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / chromeos / net / network_portal_detector_impl.cc
blobd1a31029f998e5eeb9ae6271ab03d1136effd267
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "chromeos/dbus/shill_profile_client.h"
18 #include "chromeos/login/login_state.h"
19 #include "chromeos/network/network_event_log.h"
20 #include "chromeos/network/network_state.h"
21 #include "chromeos/network/network_state_handler.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/common/content_switches.h"
24 #include "net/http/http_status_code.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
27 using base::StringPrintf;
28 using captive_portal::CaptivePortalDetector;
30 namespace chromeos {
32 namespace {
34 // Delay before portal detection caused by changes in proxy settings.
35 const int kProxyChangeDelaySec = 1;
37 // Maximum number of reports from captive portal detector about
38 // offline state in a row before notification is sent to observers.
39 const int kMaxOfflineResultsBeforeReport = 3;
41 // Delay before portal detection attempt after !ONLINE -> !ONLINE
42 // transition.
43 const int kShortInitialDelayBetweenAttemptsMs = 600;
45 // Maximum timeout before portal detection attempts after !ONLINE ->
46 // !ONLINE transition.
47 const int kShortMaximumDelayBetweenAttemptsMs = 2 * 60 * 1000;
49 // Delay before portal detection attempt after !ONLINE -> ONLINE
50 // transition.
51 const int kLongInitialDelayBetweenAttemptsMs = 30 * 1000;
53 // Maximum timeout before portal detection attempts after !ONLINE ->
54 // ONLINE transition.
55 const int kLongMaximumDelayBetweenAttemptsMs = 5 * 60 * 1000;
57 const NetworkState* DefaultNetwork() {
58 return NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
61 bool InSession() {
62 return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn();
65 void RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) {
66 if (InSession()) {
67 UMA_HISTOGRAM_ENUMERATION(
68 NetworkPortalDetectorImpl::kSessionDetectionResultHistogram,
69 status,
70 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
71 } else {
72 UMA_HISTOGRAM_ENUMERATION(
73 NetworkPortalDetectorImpl::kOobeDetectionResultHistogram,
74 status,
75 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
79 void RecordDetectionDuration(const base::TimeDelta& duration) {
80 if (InSession()) {
81 UMA_HISTOGRAM_MEDIUM_TIMES(
82 NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram,
83 duration);
84 } else {
85 UMA_HISTOGRAM_MEDIUM_TIMES(
86 NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram, duration);
90 void RecordDiscrepancyWithShill(
91 const NetworkState* network,
92 const NetworkPortalDetector::CaptivePortalStatus status) {
93 if (InSession()) {
94 if (network->connection_state() == shill::kStateOnline) {
95 UMA_HISTOGRAM_ENUMERATION(
96 NetworkPortalDetectorImpl::kSessionShillOnlineHistogram,
97 status,
98 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
99 } else if (network->connection_state() == shill::kStatePortal) {
100 UMA_HISTOGRAM_ENUMERATION(
101 NetworkPortalDetectorImpl::kSessionShillPortalHistogram,
102 status,
103 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
104 } else if (network->connection_state() == shill::kStateOffline) {
105 UMA_HISTOGRAM_ENUMERATION(
106 NetworkPortalDetectorImpl::kSessionShillOfflineHistogram,
107 status,
108 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
110 } else {
111 if (network->connection_state() == shill::kStateOnline) {
112 UMA_HISTOGRAM_ENUMERATION(
113 NetworkPortalDetectorImpl::kOobeShillOnlineHistogram,
114 status,
115 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
116 } else if (network->connection_state() == shill::kStatePortal) {
117 UMA_HISTOGRAM_ENUMERATION(
118 NetworkPortalDetectorImpl::kOobeShillPortalHistogram,
119 status,
120 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
121 } else if (network->connection_state() == shill::kStateOffline) {
122 UMA_HISTOGRAM_ENUMERATION(
123 NetworkPortalDetectorImpl::kOobeShillOfflineHistogram,
124 status,
125 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT);
130 void RecordPortalToOnlineTransition(const base::TimeDelta& duration) {
131 if (InSession()) {
132 UMA_HISTOGRAM_LONG_TIMES(
133 NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram,
134 duration);
135 } else {
136 UMA_HISTOGRAM_LONG_TIMES(
137 NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram,
138 duration);
142 } // namespace
144 ////////////////////////////////////////////////////////////////////////////////
145 // NetworkPortalDetectorImpl::DetectionAttemptCompletedLogState
147 NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
148 DetectionAttemptCompletedReport()
149 : result(captive_portal::RESULT_COUNT), response_code(-1) {
152 NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::
153 DetectionAttemptCompletedReport(const std::string network_name,
154 const std::string network_id,
155 captive_portal::CaptivePortalResult result,
156 int response_code)
157 : network_name(network_name),
158 network_id(network_id),
159 result(result),
160 response_code(response_code) {
163 void NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Report()
164 const {
165 // TODO (ygorshenin@): remove VLOG as soon as NET_LOG_EVENT will be dumped on
166 // a disk, crbug.com/293739.
167 VLOG(1) << "Detection attempt completed: "
168 << "name=" << network_name << ", "
169 << "id=" << network_id << ", "
170 << "result=" << captive_portal::CaptivePortalResultToString(result)
171 << ", "
172 << "response_code=" << response_code;
173 NET_LOG_EVENT(StringPrintf(
174 "Portal detection completed: network_id=%s, result=%s, "
175 "response_code=%d",
176 network_id.c_str(),
177 captive_portal::CaptivePortalResultToString(result).c_str(),
178 response_code),
179 network_name);
182 bool NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Equals(
183 const DetectionAttemptCompletedReport& o) const {
184 return network_name == o.network_name && network_id == o.network_id &&
185 result == o.result && response_code == o.response_code;
188 ////////////////////////////////////////////////////////////////////////////////
189 // NetworkPortalDetectorImpl, public:
191 const char NetworkPortalDetectorImpl::kOobeDetectionResultHistogram[] =
192 "CaptivePortal.OOBE.DetectionResult";
193 const char NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram[] =
194 "CaptivePortal.OOBE.DetectionDuration";
195 const char NetworkPortalDetectorImpl::kOobeShillOnlineHistogram[] =
196 "CaptivePortal.OOBE.DiscrepancyWithShill_Online";
197 const char NetworkPortalDetectorImpl::kOobeShillPortalHistogram[] =
198 "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool";
199 const char NetworkPortalDetectorImpl::kOobeShillOfflineHistogram[] =
200 "CaptivePortal.OOBE.DiscrepancyWithShill_Offline";
201 const char NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram[] =
202 "CaptivePortal.OOBE.PortalToOnlineTransition";
204 const char NetworkPortalDetectorImpl::kSessionDetectionResultHistogram[] =
205 "CaptivePortal.Session.DetectionResult";
206 const char NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram[] =
207 "CaptivePortal.Session.DetectionDuration";
208 const char NetworkPortalDetectorImpl::kSessionShillOnlineHistogram[] =
209 "CaptivePortal.Session.DiscrepancyWithShill_Online";
210 const char NetworkPortalDetectorImpl::kSessionShillPortalHistogram[] =
211 "CaptivePortal.Session.DiscrepancyWithShill_RestrictedPool";
212 const char NetworkPortalDetectorImpl::kSessionShillOfflineHistogram[] =
213 "CaptivePortal.Session.DiscrepancyWithShill_Offline";
214 const char NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram[] =
215 "CaptivePortal.Session.PortalToOnlineTransition";
217 // static
218 void NetworkPortalDetectorImpl::Initialize(
219 net::URLRequestContextGetter* url_context) {
220 if (NetworkPortalDetector::set_for_testing())
221 return;
222 CHECK(!NetworkPortalDetector::network_portal_detector())
223 << "NetworkPortalDetector was initialized twice.";
224 if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
225 set_network_portal_detector(new NetworkPortalDetectorStubImpl());
226 else
227 set_network_portal_detector(new NetworkPortalDetectorImpl(url_context));
230 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl(
231 const scoped_refptr<net::URLRequestContextGetter>& request_context)
232 : state_(STATE_IDLE),
233 test_url_(CaptivePortalDetector::kDefaultURL),
234 enabled_(false),
235 strategy_(PortalDetectorStrategy::CreateById(
236 PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN, this)),
237 last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN),
238 same_detection_result_count_(0),
239 no_response_result_count_(0),
240 weak_factory_(this) {
241 captive_portal_detector_.reset(new CaptivePortalDetector(request_context));
243 registrar_.Add(this,
244 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
245 content::NotificationService::AllSources());
246 registrar_.Add(this,
247 chrome::NOTIFICATION_AUTH_SUPPLIED,
248 content::NotificationService::AllSources());
249 registrar_.Add(this,
250 chrome::NOTIFICATION_AUTH_CANCELLED,
251 content::NotificationService::AllSources());
253 NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
254 StartDetectionIfIdle();
257 NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() {
258 DCHECK(CalledOnValidThread());
260 attempt_task_.Cancel();
261 attempt_timeout_.Cancel();
263 captive_portal_detector_->Cancel();
264 captive_portal_detector_.reset();
265 observers_.Clear();
266 if (NetworkHandler::IsInitialized()) {
267 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
268 FROM_HERE);
272 void NetworkPortalDetectorImpl::AddObserver(Observer* observer) {
273 DCHECK(CalledOnValidThread());
274 if (observer && !observers_.HasObserver(observer))
275 observers_.AddObserver(observer);
278 void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) {
279 DCHECK(CalledOnValidThread());
280 if (!observer)
281 return;
282 AddObserver(observer);
283 CaptivePortalState portal_state;
284 const NetworkState* network = DefaultNetwork();
285 if (network)
286 portal_state = GetCaptivePortalState(network->guid());
287 observer->OnPortalDetectionCompleted(network, portal_state);
290 void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) {
291 DCHECK(CalledOnValidThread());
292 if (observer)
293 observers_.RemoveObserver(observer);
296 bool NetworkPortalDetectorImpl::IsEnabled() { return enabled_; }
298 void NetworkPortalDetectorImpl::Enable(bool start_detection) {
299 DCHECK(CalledOnValidThread());
300 if (enabled_)
301 return;
303 DCHECK(is_idle());
304 enabled_ = true;
306 const NetworkState* network = DefaultNetwork();
307 if (!start_detection || !network)
308 return;
309 NET_LOG_EVENT(StringPrintf("Starting detection attempt: network_id=%s",
310 network->guid().c_str()),
311 network->name());
312 portal_state_map_.erase(network->guid());
313 StartDetection();
316 NetworkPortalDetectorImpl::CaptivePortalState
317 NetworkPortalDetectorImpl::GetCaptivePortalState(const std::string& guid) {
318 DCHECK(CalledOnValidThread());
319 CaptivePortalStateMap::const_iterator it = portal_state_map_.find(guid);
320 if (it == portal_state_map_.end())
321 return CaptivePortalState();
322 return it->second;
325 bool NetworkPortalDetectorImpl::StartDetectionIfIdle() {
326 if (!is_idle())
327 return false;
328 StartDetection();
329 return true;
332 void NetworkPortalDetectorImpl::SetStrategy(
333 PortalDetectorStrategy::StrategyId id) {
334 if (id == strategy_->Id())
335 return;
336 strategy_ = PortalDetectorStrategy::CreateById(id, this).Pass();
337 StopDetection();
338 StartDetectionIfIdle();
341 void NetworkPortalDetectorImpl::DefaultNetworkChanged(
342 const NetworkState* default_network) {
343 DCHECK(CalledOnValidThread());
345 if (!default_network) {
346 NET_LOG_EVENT("Default network changed", "None");
348 default_network_name_.clear();
350 StopDetection();
352 CaptivePortalState state;
353 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
354 OnDetectionCompleted(NULL, state);
355 return;
358 default_network_name_ = default_network->name();
360 bool network_changed = (default_network_id_ != default_network->guid());
361 default_network_id_ = default_network->guid();
363 bool connection_state_changed =
364 (default_connection_state_ != default_network->connection_state());
365 default_connection_state_ = default_network->connection_state();
367 NET_LOG_EVENT(StringPrintf(
368 "Default network changed: network_id=%s, state=%s, "
369 "changed=%d, state_changed=%d",
370 default_network_id_.c_str(),
371 default_connection_state_.c_str(),
372 network_changed,
373 connection_state_changed),
374 default_network_name_);
376 if (network_changed || connection_state_changed)
377 StopDetection();
379 if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) {
380 // Initiate Captive Portal detection if network's captive
381 // portal state is unknown (e.g. for freshly created networks),
382 // offline or if network connection state was changed.
383 CaptivePortalState state = GetCaptivePortalState(default_network->guid());
384 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN ||
385 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE ||
386 (!network_changed && connection_state_changed)) {
387 ScheduleAttempt(base::TimeDelta());
392 int NetworkPortalDetectorImpl::NoResponseResultCount() {
393 return no_response_result_count_;
396 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() {
397 return attempt_start_time_;
400 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() {
401 if (time_ticks_for_testing_.is_null())
402 return base::TimeTicks::Now();
403 return time_ticks_for_testing_;
407 ////////////////////////////////////////////////////////////////////////////////
408 // NetworkPortalDetectorImpl, private:
410 void NetworkPortalDetectorImpl::StartDetection() {
411 DCHECK(is_idle());
413 ResetStrategyAndCounters();
414 detection_start_time_ = GetCurrentTimeTicks();
415 ScheduleAttempt(base::TimeDelta());
418 void NetworkPortalDetectorImpl::StopDetection() {
419 attempt_task_.Cancel();
420 attempt_timeout_.Cancel();
421 captive_portal_detector_->Cancel();
422 state_ = STATE_IDLE;
423 ResetStrategyAndCounters();
426 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) {
427 DCHECK(is_idle());
429 if (!IsEnabled())
430 return;
432 attempt_task_.Cancel();
433 attempt_timeout_.Cancel();
434 state_ = STATE_PORTAL_CHECK_PENDING;
436 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt());
437 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt,
438 weak_factory_.GetWeakPtr()));
439 base::MessageLoop::current()->PostDelayedTask(
440 FROM_HERE, attempt_task_.callback(), next_attempt_delay_);
443 void NetworkPortalDetectorImpl::StartAttempt() {
444 DCHECK(is_portal_check_pending());
446 state_ = STATE_CHECKING_FOR_PORTAL;
447 attempt_start_time_ = GetCurrentTimeTicks();
449 captive_portal_detector_->DetectCaptivePortal(
450 test_url_,
451 base::Bind(&NetworkPortalDetectorImpl::OnAttemptCompleted,
452 weak_factory_.GetWeakPtr()));
453 attempt_timeout_.Reset(
454 base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout,
455 weak_factory_.GetWeakPtr()));
457 base::MessageLoop::current()->PostDelayedTask(
458 FROM_HERE,
459 attempt_timeout_.callback(),
460 strategy_->GetNextAttemptTimeout());
463 void NetworkPortalDetectorImpl::OnAttemptTimeout() {
464 DCHECK(CalledOnValidThread());
465 DCHECK(is_checking_for_portal());
467 NET_LOG_ERROR(StringPrintf("Portal detection timeout: network_id=%s",
468 default_network_id_.c_str()),
469 default_network_name_);
471 captive_portal_detector_->Cancel();
472 CaptivePortalDetector::Results results;
473 results.result = captive_portal::RESULT_NO_RESPONSE;
474 OnAttemptCompleted(results);
477 void NetworkPortalDetectorImpl::OnAttemptCompleted(
478 const CaptivePortalDetector::Results& results) {
479 DCHECK(CalledOnValidThread());
480 DCHECK(is_checking_for_portal());
482 captive_portal::CaptivePortalResult result = results.result;
483 int response_code = results.response_code;
485 const NetworkState* network = DefaultNetwork();
487 // If using a fake profile client, also fake being behind a captive portal
488 // if the default network is in portal state.
489 if (result != captive_portal::RESULT_NO_RESPONSE &&
490 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() &&
491 network && network->connection_state() == shill::kStatePortal) {
492 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
493 response_code = 200;
496 DetectionAttemptCompletedReport attempt_completed_report(
497 default_network_name_, default_network_id_, result, response_code);
498 if (!attempt_completed_report_.Equals(attempt_completed_report)) {
499 attempt_completed_report_ = attempt_completed_report;
500 attempt_completed_report_.Report();
503 state_ = STATE_IDLE;
504 attempt_timeout_.Cancel();
506 CaptivePortalState state;
507 state.response_code = response_code;
508 state.time = GetCurrentTimeTicks();
509 switch (result) {
510 case captive_portal::RESULT_NO_RESPONSE:
511 if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
512 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED;
513 } else if (network &&
514 (network->connection_state() == shill::kStatePortal)) {
515 // Take into account shill's detection results.
516 state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
517 } else {
518 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
520 break;
521 case captive_portal::RESULT_INTERNET_CONNECTED:
522 state.status = CAPTIVE_PORTAL_STATUS_ONLINE;
523 break;
524 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
525 state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
526 break;
527 default:
528 break;
531 if (last_detection_result_ != state.status) {
532 last_detection_result_ = state.status;
533 same_detection_result_count_ = 1;
534 net::BackoffEntry::Policy policy = strategy_->policy();
535 if (state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
536 policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs;
537 policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs;
538 } else {
539 policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs;
540 policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs;
542 strategy_->SetPolicyAndReset(policy);
543 } else {
544 ++same_detection_result_count_;
546 strategy_->OnDetectionCompleted();
548 if (result == captive_portal::RESULT_NO_RESPONSE)
549 ++no_response_result_count_;
550 else
551 no_response_result_count_ = 0;
553 if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE ||
554 same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) {
555 OnDetectionCompleted(network, state);
557 ScheduleAttempt(results.retry_after_delta);
560 void NetworkPortalDetectorImpl::Observe(
561 int type,
562 const content::NotificationSource& source,
563 const content::NotificationDetails& details) {
564 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED ||
565 type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
566 type == chrome::NOTIFICATION_AUTH_CANCELLED) {
567 NET_LOG_EVENT(
568 "Restarting portal detection due to proxy change",
569 default_network_name_.empty() ? "None" : default_network_name_);
570 StopDetection();
571 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
575 void NetworkPortalDetectorImpl::OnDetectionCompleted(
576 const NetworkState* network,
577 const CaptivePortalState& state) {
578 if (!network) {
579 NotifyDetectionCompleted(network, state);
580 return;
583 CaptivePortalStateMap::const_iterator it =
584 portal_state_map_.find(network->guid());
585 if (it == portal_state_map_.end() || it->second.status != state.status ||
586 it->second.response_code != state.response_code) {
587 // Record detection duration iff detection result differs from the
588 // previous one for this network. The reason is to record all stats
589 // only when network changes it's state.
590 RecordDetectionStats(network, state.status);
591 if (it != portal_state_map_.end() &&
592 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL &&
593 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
594 RecordPortalToOnlineTransition(state.time - it->second.time);
597 portal_state_map_[network->guid()] = state;
599 NotifyDetectionCompleted(network, state);
602 void NetworkPortalDetectorImpl::NotifyDetectionCompleted(
603 const NetworkState* network,
604 const CaptivePortalState& state) {
605 FOR_EACH_OBSERVER(
606 Observer, observers_, OnPortalDetectionCompleted(network, state));
607 notification_controller_.OnPortalDetectionCompleted(network, state);
610 bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const {
611 return attempt_timeout_.IsCancelled();
614 void NetworkPortalDetectorImpl::RecordDetectionStats(
615 const NetworkState* network,
616 CaptivePortalStatus status) {
617 // Don't record stats for offline state.
618 if (!network)
619 return;
621 if (!detection_start_time_.is_null())
622 RecordDetectionDuration(GetCurrentTimeTicks() - detection_start_time_);
623 RecordDetectionResult(status);
625 switch (status) {
626 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
627 NOTREACHED();
628 break;
629 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
630 if (network->connection_state() == shill::kStateOnline ||
631 network->connection_state() == shill::kStatePortal) {
632 RecordDiscrepancyWithShill(network, status);
634 break;
635 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
636 if (network->connection_state() != shill::kStateOnline)
637 RecordDiscrepancyWithShill(network, status);
638 break;
639 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
640 if (network->connection_state() != shill::kStatePortal)
641 RecordDiscrepancyWithShill(network, status);
642 break;
643 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
644 if (network->connection_state() != shill::kStateOnline)
645 RecordDiscrepancyWithShill(network, status);
646 break;
647 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
648 NOTREACHED();
649 break;
653 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() {
654 last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
655 same_detection_result_count_ = 0;
656 no_response_result_count_ = 0;
657 strategy_->Reset();
660 } // namespace chromeos