Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / net / network_portal_detector_impl.cc
blobe58698103d432c11710eb725c0029d14839210ee
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_state.h"
20 #include "chromeos/network/network_state_handler.h"
21 #include "components/device_event_log/device_event_log.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->is_captive_portal()) {
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->is_captive_portal()) {
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 // To see NET_LOG output, use '--vmodule=device_event_log*=1'
166 NET_LOG(EVENT) << "Detection attempt completed: "
167 << "name=" << network_name << ", "
168 << "id=" << network_id << ", "
169 << "result="
170 << captive_portal::CaptivePortalResultToString(result) << ", "
171 << "response_code=" << response_code;
174 bool NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Equals(
175 const DetectionAttemptCompletedReport& o) const {
176 return network_name == o.network_name && network_id == o.network_id &&
177 result == o.result && response_code == o.response_code;
180 ////////////////////////////////////////////////////////////////////////////////
181 // NetworkPortalDetectorImpl, public:
183 const char NetworkPortalDetectorImpl::kOobeDetectionResultHistogram[] =
184 "CaptivePortal.OOBE.DetectionResult";
185 const char NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram[] =
186 "CaptivePortal.OOBE.DetectionDuration";
187 const char NetworkPortalDetectorImpl::kOobeShillOnlineHistogram[] =
188 "CaptivePortal.OOBE.DiscrepancyWithShill_Online";
189 const char NetworkPortalDetectorImpl::kOobeShillPortalHistogram[] =
190 "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool";
191 const char NetworkPortalDetectorImpl::kOobeShillOfflineHistogram[] =
192 "CaptivePortal.OOBE.DiscrepancyWithShill_Offline";
193 const char NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram[] =
194 "CaptivePortal.OOBE.PortalToOnlineTransition";
196 const char NetworkPortalDetectorImpl::kSessionDetectionResultHistogram[] =
197 "CaptivePortal.Session.DetectionResult";
198 const char NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram[] =
199 "CaptivePortal.Session.DetectionDuration";
200 const char NetworkPortalDetectorImpl::kSessionShillOnlineHistogram[] =
201 "CaptivePortal.Session.DiscrepancyWithShill_Online";
202 const char NetworkPortalDetectorImpl::kSessionShillPortalHistogram[] =
203 "CaptivePortal.Session.DiscrepancyWithShill_RestrictedPool";
204 const char NetworkPortalDetectorImpl::kSessionShillOfflineHistogram[] =
205 "CaptivePortal.Session.DiscrepancyWithShill_Offline";
206 const char NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram[] =
207 "CaptivePortal.Session.PortalToOnlineTransition";
209 // static
210 void NetworkPortalDetectorImpl::Initialize(
211 net::URLRequestContextGetter* url_context) {
212 if (NetworkPortalDetector::set_for_testing())
213 return;
214 CHECK(!NetworkPortalDetector::network_portal_detector())
215 << "NetworkPortalDetector was initialized twice.";
216 NET_LOG(EVENT) << "NetworkPortalDetectorImpl::Initialize()";
217 if (base::CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
218 set_network_portal_detector(new NetworkPortalDetectorStubImpl());
219 else
220 set_network_portal_detector(new NetworkPortalDetectorImpl(url_context));
223 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl(
224 const scoped_refptr<net::URLRequestContextGetter>& request_context)
225 : state_(STATE_IDLE),
226 test_url_(CaptivePortalDetector::kDefaultURL),
227 enabled_(false),
228 strategy_(PortalDetectorStrategy::CreateById(
229 PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN,
230 this)),
231 last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN),
232 same_detection_result_count_(0),
233 no_response_result_count_(0),
234 weak_factory_(this) {
235 NET_LOG(EVENT) << "NetworkPortalDetectorImpl::NetworkPortalDetectorImpl()";
236 captive_portal_detector_.reset(new CaptivePortalDetector(request_context));
238 notification_controller_.set_retry_detection_callback(base::Bind(
239 &NetworkPortalDetectorImpl::RetryDetection, base::Unretained(this)));
241 registrar_.Add(this,
242 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
243 content::NotificationService::AllSources());
244 registrar_.Add(this,
245 chrome::NOTIFICATION_AUTH_SUPPLIED,
246 content::NotificationService::AllSources());
247 registrar_.Add(this,
248 chrome::NOTIFICATION_AUTH_CANCELLED,
249 content::NotificationService::AllSources());
251 NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
252 StartDetectionIfIdle();
255 NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() {
256 NET_LOG(EVENT) << "NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl()";
257 DCHECK(CalledOnValidThread());
259 attempt_task_.Cancel();
260 attempt_timeout_.Cancel();
262 captive_portal_detector_->Cancel();
263 captive_portal_detector_.reset();
264 observers_.Clear();
265 if (NetworkHandler::IsInitialized()) {
266 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
267 FROM_HERE);
271 void NetworkPortalDetectorImpl::AddObserver(Observer* observer) {
272 DCHECK(CalledOnValidThread());
273 if (observer && !observers_.HasObserver(observer))
274 observers_.AddObserver(observer);
277 void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) {
278 DCHECK(CalledOnValidThread());
279 if (!observer)
280 return;
281 AddObserver(observer);
282 CaptivePortalState portal_state;
283 const NetworkState* network = DefaultNetwork();
284 if (network)
285 portal_state = GetCaptivePortalState(network->guid());
286 observer->OnPortalDetectionCompleted(network, portal_state);
289 void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) {
290 DCHECK(CalledOnValidThread());
291 if (observer)
292 observers_.RemoveObserver(observer);
295 bool NetworkPortalDetectorImpl::IsEnabled() { return enabled_; }
297 void NetworkPortalDetectorImpl::Enable(bool start_detection) {
298 DCHECK(CalledOnValidThread());
299 if (enabled_)
300 return;
302 DCHECK(is_idle());
303 enabled_ = true;
305 const NetworkState* network = DefaultNetwork();
306 if (!start_detection || !network)
307 return;
308 NET_LOG(EVENT) << "Starting detection attempt:"
309 << " name=" << network->name() << " id=" << network->guid();
310 portal_state_map_.erase(network->guid());
311 StartDetection();
314 NetworkPortalDetectorImpl::CaptivePortalState
315 NetworkPortalDetectorImpl::GetCaptivePortalState(const std::string& guid) {
316 DCHECK(CalledOnValidThread());
317 CaptivePortalStateMap::const_iterator it = portal_state_map_.find(guid);
318 if (it == portal_state_map_.end())
319 return CaptivePortalState();
320 return it->second;
323 bool NetworkPortalDetectorImpl::StartDetectionIfIdle() {
324 if (!is_idle())
325 return false;
326 StartDetection();
327 return true;
330 void NetworkPortalDetectorImpl::SetStrategy(
331 PortalDetectorStrategy::StrategyId id) {
332 if (id == strategy_->Id())
333 return;
334 strategy_ = PortalDetectorStrategy::CreateById(id, this).Pass();
335 StopDetection();
336 StartDetectionIfIdle();
339 void NetworkPortalDetectorImpl::OnLockScreenRequest() {
340 notification_controller_.CloseDialog();
343 void NetworkPortalDetectorImpl::DefaultNetworkChanged(
344 const NetworkState* default_network) {
345 DCHECK(CalledOnValidThread());
347 notification_controller_.DefaultNetworkChanged(default_network);
348 if (!default_network) {
349 NET_LOG(EVENT) << "Default network changed: None";
351 default_network_name_.clear();
353 StopDetection();
355 CaptivePortalState state;
356 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
357 OnDetectionCompleted(NULL, state);
358 return;
361 default_network_name_ = default_network->name();
363 bool network_changed = (default_network_id_ != default_network->guid());
364 default_network_id_ = default_network->guid();
366 bool connection_state_changed =
367 (default_connection_state_ != default_network->connection_state());
368 default_connection_state_ = default_network->connection_state();
370 NET_LOG(EVENT) << "Default network changed:"
371 << " name=" << default_network_name_
372 << " id=" << default_network_id_
373 << " state=" << default_connection_state_
374 << " changed=" << network_changed
375 << " state_changed=" << connection_state_changed;
377 if (network_changed || connection_state_changed)
378 StopDetection();
380 if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) {
381 // Initiate Captive Portal detection if network's captive
382 // portal state is unknown (e.g. for freshly created networks),
383 // offline or if network connection state was changed.
384 CaptivePortalState state = GetCaptivePortalState(default_network->guid());
385 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN ||
386 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE ||
387 (!network_changed && connection_state_changed)) {
388 ScheduleAttempt(base::TimeDelta());
393 int NetworkPortalDetectorImpl::NoResponseResultCount() {
394 return no_response_result_count_;
397 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() {
398 return attempt_start_time_;
401 base::TimeTicks NetworkPortalDetectorImpl::NowTicks() {
402 if (time_ticks_for_testing_.is_null())
403 return base::TimeTicks::Now();
404 return time_ticks_for_testing_;
408 ////////////////////////////////////////////////////////////////////////////////
409 // NetworkPortalDetectorImpl, private:
411 void NetworkPortalDetectorImpl::StartDetection() {
412 DCHECK(is_idle());
414 ResetStrategyAndCounters();
415 detection_start_time_ = NowTicks();
416 ScheduleAttempt(base::TimeDelta());
419 void NetworkPortalDetectorImpl::StopDetection() {
420 attempt_task_.Cancel();
421 attempt_timeout_.Cancel();
422 captive_portal_detector_->Cancel();
423 state_ = STATE_IDLE;
424 ResetStrategyAndCounters();
427 void NetworkPortalDetectorImpl::RetryDetection() {
428 StopDetection();
429 StartDetection();
432 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) {
433 DCHECK(is_idle());
435 if (!IsEnabled())
436 return;
438 attempt_task_.Cancel();
439 attempt_timeout_.Cancel();
440 state_ = STATE_PORTAL_CHECK_PENDING;
442 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt());
443 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt,
444 weak_factory_.GetWeakPtr()));
445 base::MessageLoop::current()->PostDelayedTask(
446 FROM_HERE, attempt_task_.callback(), next_attempt_delay_);
449 void NetworkPortalDetectorImpl::StartAttempt() {
450 DCHECK(is_portal_check_pending());
452 state_ = STATE_CHECKING_FOR_PORTAL;
453 attempt_start_time_ = NowTicks();
455 captive_portal_detector_->DetectCaptivePortal(
456 test_url_,
457 base::Bind(&NetworkPortalDetectorImpl::OnAttemptCompleted,
458 weak_factory_.GetWeakPtr()));
459 attempt_timeout_.Reset(
460 base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout,
461 weak_factory_.GetWeakPtr()));
463 base::MessageLoop::current()->PostDelayedTask(
464 FROM_HERE,
465 attempt_timeout_.callback(),
466 strategy_->GetNextAttemptTimeout());
469 void NetworkPortalDetectorImpl::OnAttemptTimeout() {
470 DCHECK(CalledOnValidThread());
471 DCHECK(is_checking_for_portal());
473 NET_LOG(ERROR) << "Portal detection timeout: "
474 << " name=" << default_network_name_
475 << " id=" << default_network_id_;
477 captive_portal_detector_->Cancel();
478 CaptivePortalDetector::Results results;
479 results.result = captive_portal::RESULT_NO_RESPONSE;
480 OnAttemptCompleted(results);
483 void NetworkPortalDetectorImpl::OnAttemptCompleted(
484 const CaptivePortalDetector::Results& results) {
485 DCHECK(CalledOnValidThread());
486 DCHECK(is_checking_for_portal());
488 captive_portal::CaptivePortalResult result = results.result;
489 int response_code = results.response_code;
491 const NetworkState* network = DefaultNetwork();
493 // If using a fake profile client, also fake being behind a captive portal
494 // if the default network is in portal state.
495 if (result != captive_portal::RESULT_NO_RESPONSE &&
496 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() &&
497 network && network->is_captive_portal()) {
498 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL;
499 response_code = 200;
502 DetectionAttemptCompletedReport attempt_completed_report(
503 default_network_name_, default_network_id_, result, response_code);
504 if (!attempt_completed_report_.Equals(attempt_completed_report)) {
505 attempt_completed_report_ = attempt_completed_report;
506 attempt_completed_report_.Report();
509 state_ = STATE_IDLE;
510 attempt_timeout_.Cancel();
512 CaptivePortalState state;
513 state.response_code = response_code;
514 state.time = NowTicks();
515 switch (result) {
516 case captive_portal::RESULT_NO_RESPONSE:
517 if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) {
518 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED;
519 } else if (network && network->is_captive_portal()) {
520 // Take into account shill's detection results.
521 state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
522 } else {
523 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE;
525 break;
526 case captive_portal::RESULT_INTERNET_CONNECTED:
527 state.status = CAPTIVE_PORTAL_STATUS_ONLINE;
528 break;
529 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL:
530 state.status = CAPTIVE_PORTAL_STATUS_PORTAL;
531 break;
532 default:
533 break;
536 if (last_detection_result_ != state.status) {
537 last_detection_result_ = state.status;
538 same_detection_result_count_ = 1;
539 net::BackoffEntry::Policy policy = strategy_->policy();
540 if (state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
541 policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs;
542 policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs;
543 } else {
544 policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs;
545 policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs;
547 strategy_->SetPolicyAndReset(policy);
548 } else {
549 ++same_detection_result_count_;
551 strategy_->OnDetectionCompleted();
553 if (result == captive_portal::RESULT_NO_RESPONSE)
554 ++no_response_result_count_;
555 else
556 no_response_result_count_ = 0;
558 if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE ||
559 same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) {
560 OnDetectionCompleted(network, state);
562 ScheduleAttempt(results.retry_after_delta);
565 void NetworkPortalDetectorImpl::Observe(
566 int type,
567 const content::NotificationSource& source,
568 const content::NotificationDetails& details) {
569 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED ||
570 type == chrome::NOTIFICATION_AUTH_SUPPLIED ||
571 type == chrome::NOTIFICATION_AUTH_CANCELLED) {
572 NET_LOG(EVENT) << "Restarting portal detection due to proxy change"
573 << " name=" << default_network_name_;
574 StopDetection();
575 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec));
579 void NetworkPortalDetectorImpl::OnDetectionCompleted(
580 const NetworkState* network,
581 const CaptivePortalState& state) {
582 if (!network) {
583 NotifyDetectionCompleted(network, state);
584 return;
587 CaptivePortalStateMap::const_iterator it =
588 portal_state_map_.find(network->guid());
589 if (it == portal_state_map_.end() || it->second.status != state.status ||
590 it->second.response_code != state.response_code) {
591 // Record detection duration iff detection result differs from the
592 // previous one for this network. The reason is to record all stats
593 // only when network changes it's state.
594 RecordDetectionStats(network, state.status);
595 if (it != portal_state_map_.end() &&
596 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL &&
597 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) {
598 RecordPortalToOnlineTransition(state.time - it->second.time);
601 portal_state_map_[network->guid()] = state;
603 NotifyDetectionCompleted(network, state);
606 void NetworkPortalDetectorImpl::NotifyDetectionCompleted(
607 const NetworkState* network,
608 const CaptivePortalState& state) {
609 FOR_EACH_OBSERVER(
610 Observer, observers_, OnPortalDetectionCompleted(network, state));
611 notification_controller_.OnPortalDetectionCompleted(network, state);
614 bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const {
615 return attempt_timeout_.IsCancelled();
618 void NetworkPortalDetectorImpl::RecordDetectionStats(
619 const NetworkState* network,
620 CaptivePortalStatus status) {
621 // Don't record stats for offline state.
622 if (!network)
623 return;
625 if (!detection_start_time_.is_null())
626 RecordDetectionDuration(NowTicks() - detection_start_time_);
627 RecordDetectionResult(status);
629 switch (status) {
630 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN:
631 NOTREACHED();
632 break;
633 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE:
634 if (network->IsConnectedState())
635 RecordDiscrepancyWithShill(network, status);
636 break;
637 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE:
638 if (network->connection_state() != shill::kStateOnline)
639 RecordDiscrepancyWithShill(network, status);
640 break;
641 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL:
642 if (!network->is_captive_portal())
643 RecordDiscrepancyWithShill(network, status);
644 break;
645 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED:
646 if (network->connection_state() != shill::kStateOnline)
647 RecordDiscrepancyWithShill(network, status);
648 break;
649 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT:
650 NOTREACHED();
651 break;
655 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() {
656 last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN;
657 same_detection_result_count_ = 0;
658 no_response_result_count_ = 0;
659 strategy_->Reset();
662 } // namespace chromeos