network_portal_detector_impl.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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. 4 5#include "chrome/browser/chromeos/net/network_portal_detector_impl.h" 6 7#include <algorithm> 8 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 "chrome/browser/chrome_notification_types.h" 15#include "chromeos/dbus/dbus_thread_manager.h" 16#include "chromeos/dbus/shill_profile_client.h" 17#include "chromeos/login/login_state.h" 18#include "chromeos/network/network_state.h" 19#include "chromeos/network/network_state_handler.h" 20#include "content/public/browser/notification_service.h" 21#include "content/public/common/content_switches.h" 22#include "grit/generated_resources.h" 23#include "net/http/http_status_code.h" 24#include "third_party/cros_system_api/dbus/service_constants.h" 25#include "ui/base/l10n/l10n_util.h" 26 27using captive_portal::CaptivePortalDetector; 28 29namespace chromeos { 30 31namespace { 32 33// Delay before portal detection caused by changes in proxy settings. 34const int kProxyChangeDelaySec = 1; 35 36// Maximum number of reports from captive portal detector about 37// offline state in a row before notification is sent to observers. 38const int kMaxOfflineResultsBeforeReport = 3; 39 40// Delay before portal detection attempt after !ONLINE -> !ONLINE 41// transition. 42const int kShortInitialDelayBetweenAttemptsMs = 600; 43 44// Maximum timeout before portal detection attempts after !ONLINE -> 45// !ONLINE transition. 46const int kShortMaximumDelayBetweenAttemptsMs = 2 * 60 * 1000; 47 48// Delay before portal detection attempt after !ONLINE -> ONLINE 49// transition. 50const int kLongInitialDelayBetweenAttemptsMs = 30 * 1000; 51 52// Maximum timeout before portal detection attempts after !ONLINE -> 53// ONLINE transition. 54const int kLongMaximumDelayBetweenAttemptsMs = 5 * 60 * 1000; 55 56const NetworkState* DefaultNetwork() { 57 return NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); 58} 59 60bool InSession() { 61 return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn(); 62} 63 64void RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) { 65 if (InSession()) { 66 UMA_HISTOGRAM_ENUMERATION( 67 NetworkPortalDetectorImpl::kSessionDetectionResultHistogram, 68 status, 69 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 70 } else { 71 UMA_HISTOGRAM_ENUMERATION( 72 NetworkPortalDetectorImpl::kOobeDetectionResultHistogram, 73 status, 74 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 75 } 76} 77 78void RecordDetectionDuration(const base::TimeDelta& duration) { 79 if (InSession()) { 80 UMA_HISTOGRAM_MEDIUM_TIMES( 81 NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram, 82 duration); 83 } else { 84 UMA_HISTOGRAM_MEDIUM_TIMES( 85 NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram, duration); 86 } 87} 88 89void RecordDiscrepancyWithShill( 90 const NetworkState* network, 91 const NetworkPortalDetector::CaptivePortalStatus status) { 92 if (InSession()) { 93 if (network->connection_state() == shill::kStateOnline) { 94 UMA_HISTOGRAM_ENUMERATION( 95 NetworkPortalDetectorImpl::kSessionShillOnlineHistogram, 96 status, 97 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 98 } else if (network->connection_state() == shill::kStatePortal) { 99 UMA_HISTOGRAM_ENUMERATION( 100 NetworkPortalDetectorImpl::kSessionShillPortalHistogram, 101 status, 102 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 103 } else if (network->connection_state() == shill::kStateOffline) { 104 UMA_HISTOGRAM_ENUMERATION( 105 NetworkPortalDetectorImpl::kSessionShillOfflineHistogram, 106 status, 107 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 108 } 109 } else { 110 if (network->connection_state() == shill::kStateOnline) { 111 UMA_HISTOGRAM_ENUMERATION( 112 NetworkPortalDetectorImpl::kOobeShillOnlineHistogram, 113 status, 114 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 115 } else if (network->connection_state() == shill::kStatePortal) { 116 UMA_HISTOGRAM_ENUMERATION( 117 NetworkPortalDetectorImpl::kOobeShillPortalHistogram, 118 status, 119 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 120 } else if (network->connection_state() == shill::kStateOffline) { 121 UMA_HISTOGRAM_ENUMERATION( 122 NetworkPortalDetectorImpl::kOobeShillOfflineHistogram, 123 status, 124 NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT); 125 } 126 } 127} 128 129void RecordPortalToOnlineTransition(const base::TimeDelta& duration) { 130 if (InSession()) { 131 UMA_HISTOGRAM_LONG_TIMES( 132 NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram, 133 duration); 134 } else { 135 UMA_HISTOGRAM_LONG_TIMES( 136 NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram, 137 duration); 138 } 139} 140 141} // namespace 142 143//////////////////////////////////////////////////////////////////////////////// 144// NetworkPortalDetectorImpl::DetectionAttemptCompletedLogState 145 146NetworkPortalDetectorImpl::DetectionAttemptCompletedReport:: 147 DetectionAttemptCompletedReport() 148 : result(captive_portal::RESULT_COUNT), response_code(-1) { 149} 150 151NetworkPortalDetectorImpl::DetectionAttemptCompletedReport:: 152 DetectionAttemptCompletedReport(const std::string network_name, 153 const std::string network_id, 154 captive_portal::CaptivePortalResult result, 155 int response_code) 156 : network_name(network_name), 157 network_id(network_id), 158 result(result), 159 response_code(response_code) { 160} 161 162void NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Report() 163 const { 164 VLOG(1) << "Detection attempt completed: " 165 << "name=" << network_name << ", " 166 << "id=" << network_id << ", " 167 << "result=" << captive_portal::CaptivePortalResultToString(result) 168 << ", " 169 << "response_code=" << response_code; 170} 171 172bool NetworkPortalDetectorImpl::DetectionAttemptCompletedReport::Equals( 173 const DetectionAttemptCompletedReport& o) const { 174 return network_name == o.network_name && network_id == o.network_id && 175 result == o.result && response_code == o.response_code; 176} 177 178//////////////////////////////////////////////////////////////////////////////// 179// NetworkPortalDetectorImpl, public: 180 181const char NetworkPortalDetectorImpl::kOobeDetectionResultHistogram[] = 182 "CaptivePortal.OOBE.DetectionResult"; 183const char NetworkPortalDetectorImpl::kOobeDetectionDurationHistogram[] = 184 "CaptivePortal.OOBE.DetectionDuration"; 185const char NetworkPortalDetectorImpl::kOobeShillOnlineHistogram[] = 186 "CaptivePortal.OOBE.DiscrepancyWithShill_Online"; 187const char NetworkPortalDetectorImpl::kOobeShillPortalHistogram[] = 188 "CaptivePortal.OOBE.DiscrepancyWithShill_RestrictedPool"; 189const char NetworkPortalDetectorImpl::kOobeShillOfflineHistogram[] = 190 "CaptivePortal.OOBE.DiscrepancyWithShill_Offline"; 191const char NetworkPortalDetectorImpl::kOobePortalToOnlineHistogram[] = 192 "CaptivePortal.OOBE.PortalToOnlineTransition"; 193 194const char NetworkPortalDetectorImpl::kSessionDetectionResultHistogram[] = 195 "CaptivePortal.Session.DetectionResult"; 196const char NetworkPortalDetectorImpl::kSessionDetectionDurationHistogram[] = 197 "CaptivePortal.Session.DetectionDuration"; 198const char NetworkPortalDetectorImpl::kSessionShillOnlineHistogram[] = 199 "CaptivePortal.Session.DiscrepancyWithShill_Online"; 200const char NetworkPortalDetectorImpl::kSessionShillPortalHistogram[] = 201 "CaptivePortal.Session.DiscrepancyWithShill_RestrictedPool"; 202const char NetworkPortalDetectorImpl::kSessionShillOfflineHistogram[] = 203 "CaptivePortal.Session.DiscrepancyWithShill_Offline"; 204const char NetworkPortalDetectorImpl::kSessionPortalToOnlineHistogram[] = 205 "CaptivePortal.Session.PortalToOnlineTransition"; 206 207// static 208void NetworkPortalDetectorImpl::Initialize( 209 net::URLRequestContextGetter* url_context) { 210 if (NetworkPortalDetector::set_for_testing()) 211 return; 212 CHECK(!NetworkPortalDetector::network_portal_detector()) 213 << "NetworkPortalDetector was initialized twice."; 214 if (CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType)) 215 set_network_portal_detector(new NetworkPortalDetectorStubImpl()); 216 else 217 set_network_portal_detector(new NetworkPortalDetectorImpl(url_context)); 218} 219 220NetworkPortalDetectorImpl::NetworkPortalDetectorImpl( 221 const scoped_refptr<net::URLRequestContextGetter>& request_context) 222 : state_(STATE_IDLE), 223 test_url_(CaptivePortalDetector::kDefaultURL), 224 enabled_(false), 225 strategy_(PortalDetectorStrategy::CreateById( 226 PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN, this)), 227 last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN), 228 same_detection_result_count_(0), 229 no_response_result_count_(0), 230 weak_factory_(this) { 231 captive_portal_detector_.reset(new CaptivePortalDetector(request_context)); 232 233 registrar_.Add(this, 234 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED, 235 content::NotificationService::AllSources()); 236 registrar_.Add(this, 237 chrome::NOTIFICATION_AUTH_SUPPLIED, 238 content::NotificationService::AllSources()); 239 registrar_.Add(this, 240 chrome::NOTIFICATION_AUTH_CANCELLED, 241 content::NotificationService::AllSources()); 242 243 NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE); 244 StartDetectionIfIdle(); 245} 246 247NetworkPortalDetectorImpl::~NetworkPortalDetectorImpl() { 248 DCHECK(CalledOnValidThread()); 249 250 attempt_task_.Cancel(); 251 attempt_timeout_.Cancel(); 252 253 captive_portal_detector_->Cancel(); 254 captive_portal_detector_.reset(); 255 observers_.Clear(); 256 if (NetworkHandler::IsInitialized()) { 257 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this, 258 FROM_HERE); 259 } 260} 261 262void NetworkPortalDetectorImpl::AddObserver(Observer* observer) { 263 DCHECK(CalledOnValidThread()); 264 if (observer && !observers_.HasObserver(observer)) 265 observers_.AddObserver(observer); 266} 267 268void NetworkPortalDetectorImpl::AddAndFireObserver(Observer* observer) { 269 DCHECK(CalledOnValidThread()); 270 if (!observer) 271 return; 272 AddObserver(observer); 273 CaptivePortalState portal_state; 274 const NetworkState* network = DefaultNetwork(); 275 if (network) 276 portal_state = GetCaptivePortalState(network->guid()); 277 observer->OnPortalDetectionCompleted(network, portal_state); 278} 279 280void NetworkPortalDetectorImpl::RemoveObserver(Observer* observer) { 281 DCHECK(CalledOnValidThread()); 282 if (observer) 283 observers_.RemoveObserver(observer); 284} 285 286bool NetworkPortalDetectorImpl::IsEnabled() { return enabled_; } 287 288void NetworkPortalDetectorImpl::Enable(bool start_detection) { 289 DCHECK(CalledOnValidThread()); 290 if (enabled_) 291 return; 292 293 DCHECK(is_idle()); 294 enabled_ = true; 295 296 const NetworkState* network = DefaultNetwork(); 297 if (!start_detection || !network) 298 return; 299 VLOG(1) << "Starting detection for: " 300 << "name=" << network->name() << ", id=" << network->guid(); 301 portal_state_map_.erase(network->guid()); 302 StartDetection(); 303} 304 305NetworkPortalDetectorImpl::CaptivePortalState 306NetworkPortalDetectorImpl::GetCaptivePortalState(const std::string& guid) { 307 DCHECK(CalledOnValidThread()); 308 CaptivePortalStateMap::const_iterator it = portal_state_map_.find(guid); 309 if (it == portal_state_map_.end()) 310 return CaptivePortalState(); 311 return it->second; 312} 313 314bool NetworkPortalDetectorImpl::StartDetectionIfIdle() { 315 if (!is_idle()) 316 return false; 317 StartDetection(); 318 return true; 319} 320 321void NetworkPortalDetectorImpl::SetStrategy( 322 PortalDetectorStrategy::StrategyId id) { 323 if (id == strategy_->Id()) 324 return; 325 strategy_ = PortalDetectorStrategy::CreateById(id, this).Pass(); 326 StopDetection(); 327 StartDetectionIfIdle(); 328} 329 330void NetworkPortalDetectorImpl::DefaultNetworkChanged( 331 const NetworkState* default_network) { 332 DCHECK(CalledOnValidThread()); 333 334 if (!default_network) { 335 VLOG(1) << "DefaultNetworkChanged: None."; 336 default_network_name_.clear(); 337 338 StopDetection(); 339 340 CaptivePortalState state; 341 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; 342 OnDetectionCompleted(NULL, state); 343 return; 344 } 345 346 default_network_name_ = default_network->name(); 347 348 bool network_changed = (default_network_id_ != default_network->guid()); 349 default_network_id_ = default_network->guid(); 350 351 bool connection_state_changed = 352 (default_connection_state_ != default_network->connection_state()); 353 default_connection_state_ = default_network->connection_state(); 354 355 VLOG(1) << "DefaultNetworkChanged: " 356 << "name=" << default_network_name_ << ", " 357 << "id=" << default_network_id_ << ", " 358 << "state=" << default_connection_state_ << ", " 359 << "changed=" << network_changed << ", " 360 << "state_changed=" << connection_state_changed; 361 362 if (network_changed || connection_state_changed) 363 StopDetection(); 364 365 if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) { 366 // Initiate Captive Portal detection if network's captive 367 // portal state is unknown (e.g. for freshly created networks), 368 // offline or if network connection state was changed. 369 CaptivePortalState state = GetCaptivePortalState(default_network->guid()); 370 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN || 371 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE || 372 (!network_changed && connection_state_changed)) { 373 ScheduleAttempt(base::TimeDelta()); 374 } 375 } 376} 377 378int NetworkPortalDetectorImpl::NoResponseResultCount() { 379 return no_response_result_count_; 380} 381 382base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() { 383 return attempt_start_time_; 384} 385 386base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() { 387 if (time_ticks_for_testing_.is_null()) 388 return base::TimeTicks::Now(); 389 return time_ticks_for_testing_; 390} 391 392 393//////////////////////////////////////////////////////////////////////////////// 394// NetworkPortalDetectorImpl, private: 395 396void NetworkPortalDetectorImpl::StartDetection() { 397 DCHECK(is_idle()); 398 399 ResetStrategyAndCounters(); 400 detection_start_time_ = GetCurrentTimeTicks(); 401 ScheduleAttempt(base::TimeDelta()); 402} 403 404void NetworkPortalDetectorImpl::StopDetection() { 405 attempt_task_.Cancel(); 406 attempt_timeout_.Cancel(); 407 captive_portal_detector_->Cancel(); 408 state_ = STATE_IDLE; 409 ResetStrategyAndCounters(); 410} 411 412void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) { 413 DCHECK(is_idle()); 414 415 if (!IsEnabled()) 416 return; 417 418 attempt_task_.Cancel(); 419 attempt_timeout_.Cancel(); 420 state_ = STATE_PORTAL_CHECK_PENDING; 421 422 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt()); 423 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt, 424 weak_factory_.GetWeakPtr())); 425 base::MessageLoop::current()->PostDelayedTask( 426 FROM_HERE, attempt_task_.callback(), next_attempt_delay_); 427} 428 429void NetworkPortalDetectorImpl::StartAttempt() { 430 DCHECK(is_portal_check_pending()); 431 432 state_ = STATE_CHECKING_FOR_PORTAL; 433 attempt_start_time_ = GetCurrentTimeTicks(); 434 435 captive_portal_detector_->DetectCaptivePortal( 436 test_url_, 437 base::Bind(&NetworkPortalDetectorImpl::OnAttemptCompleted, 438 weak_factory_.GetWeakPtr())); 439 attempt_timeout_.Reset( 440 base::Bind(&NetworkPortalDetectorImpl::OnAttemptTimeout, 441 weak_factory_.GetWeakPtr())); 442 443 base::MessageLoop::current()->PostDelayedTask( 444 FROM_HERE, 445 attempt_timeout_.callback(), 446 strategy_->GetNextAttemptTimeout()); 447} 448 449void NetworkPortalDetectorImpl::OnAttemptTimeout() { 450 DCHECK(CalledOnValidThread()); 451 DCHECK(is_checking_for_portal()); 452 453 VLOG(1) << "Portal detection timeout: name=" << default_network_name_ << ", " 454 << "id=" << default_network_id_; 455 456 captive_portal_detector_->Cancel(); 457 CaptivePortalDetector::Results results; 458 results.result = captive_portal::RESULT_NO_RESPONSE; 459 OnAttemptCompleted(results); 460} 461 462void NetworkPortalDetectorImpl::OnAttemptCompleted( 463 const CaptivePortalDetector::Results& results) { 464 DCHECK(CalledOnValidThread()); 465 DCHECK(is_checking_for_portal()); 466 467 captive_portal::CaptivePortalResult result = results.result; 468 int response_code = results.response_code; 469 470 const NetworkState* network = DefaultNetwork(); 471 472 // If using a fake profile client, also fake being behind a captive portal 473 // if the default network is in portal state. 474 if (result != captive_portal::RESULT_NO_RESPONSE && 475 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() && 476 network && network->connection_state() == shill::kStatePortal) { 477 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL; 478 response_code = 200; 479 } 480 481 DetectionAttemptCompletedReport attempt_completed_report( 482 default_network_name_, default_network_id_, result, response_code); 483 if (!attempt_completed_report_.Equals(attempt_completed_report)) { 484 attempt_completed_report_ = attempt_completed_report; 485 attempt_completed_report_.Report(); 486 } 487 488 state_ = STATE_IDLE; 489 attempt_timeout_.Cancel(); 490 491 CaptivePortalState state; 492 state.response_code = response_code; 493 state.time = GetCurrentTimeTicks(); 494 switch (result) { 495 case captive_portal::RESULT_NO_RESPONSE: 496 if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) { 497 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED; 498 } else if (network && 499 (network->connection_state() == shill::kStatePortal)) { 500 // Take into account shill's detection results. 501 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; 502 } else { 503 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; 504 } 505 break; 506 case captive_portal::RESULT_INTERNET_CONNECTED: 507 state.status = CAPTIVE_PORTAL_STATUS_ONLINE; 508 break; 509 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: 510 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; 511 break; 512 default: 513 break; 514 } 515 516 if (last_detection_result_ != state.status) { 517 last_detection_result_ = state.status; 518 same_detection_result_count_ = 1; 519 net::BackoffEntry::Policy policy = strategy_->policy(); 520 if (state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { 521 policy.initial_delay_ms = kLongInitialDelayBetweenAttemptsMs; 522 policy.maximum_backoff_ms = kLongMaximumDelayBetweenAttemptsMs; 523 } else { 524 policy.initial_delay_ms = kShortInitialDelayBetweenAttemptsMs; 525 policy.maximum_backoff_ms = kShortMaximumDelayBetweenAttemptsMs; 526 } 527 strategy_->SetPolicyAndReset(policy); 528 } else { 529 ++same_detection_result_count_; 530 } 531 strategy_->OnDetectionCompleted(); 532 533 if (result == captive_portal::RESULT_NO_RESPONSE) 534 ++no_response_result_count_; 535 else 536 no_response_result_count_ = 0; 537 538 if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE || 539 same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) { 540 OnDetectionCompleted(network, state); 541 } 542 ScheduleAttempt(results.retry_after_delta); 543} 544 545void NetworkPortalDetectorImpl::Observe( 546 int type, 547 const content::NotificationSource& source, 548 const content::NotificationDetails& details) { 549 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED || 550 type == chrome::NOTIFICATION_AUTH_SUPPLIED || 551 type == chrome::NOTIFICATION_AUTH_CANCELLED) { 552 VLOG(1) << "Restarting portal detection due to proxy change."; 553 StopDetection(); 554 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec)); 555 } 556} 557 558void NetworkPortalDetectorImpl::OnDetectionCompleted( 559 const NetworkState* network, 560 const CaptivePortalState& state) { 561 if (!network) { 562 NotifyDetectionCompleted(network, state); 563 return; 564 } 565 566 CaptivePortalStateMap::const_iterator it = 567 portal_state_map_.find(network->guid()); 568 if (it == portal_state_map_.end() || it->second.status != state.status || 569 it->second.response_code != state.response_code) { 570 // Record detection duration iff detection result differs from the 571 // previous one for this network. The reason is to record all stats 572 // only when network changes it's state. 573 RecordDetectionStats(network, state.status); 574 if (it != portal_state_map_.end() && 575 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL && 576 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { 577 RecordPortalToOnlineTransition(state.time - it->second.time); 578 } 579 580 portal_state_map_[network->guid()] = state; 581 } 582 NotifyDetectionCompleted(network, state); 583} 584 585void NetworkPortalDetectorImpl::NotifyDetectionCompleted( 586 const NetworkState* network, 587 const CaptivePortalState& state) { 588 FOR_EACH_OBSERVER( 589 Observer, observers_, OnPortalDetectionCompleted(network, state)); 590 notification_controller_.OnPortalDetectionCompleted(network, state); 591} 592 593bool NetworkPortalDetectorImpl::AttemptTimeoutIsCancelledForTesting() const { 594 return attempt_timeout_.IsCancelled(); 595} 596 597void NetworkPortalDetectorImpl::RecordDetectionStats( 598 const NetworkState* network, 599 CaptivePortalStatus status) { 600 // Don't record stats for offline state. 601 if (!network) 602 return; 603 604 if (!detection_start_time_.is_null()) 605 RecordDetectionDuration(GetCurrentTimeTicks() - detection_start_time_); 606 RecordDetectionResult(status); 607 608 switch (status) { 609 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN: 610 NOTREACHED(); 611 break; 612 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_OFFLINE: 613 if (network->connection_state() == shill::kStateOnline || 614 network->connection_state() == shill::kStatePortal) { 615 RecordDiscrepancyWithShill(network, status); 616 } 617 break; 618 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE: 619 if (network->connection_state() != shill::kStateOnline) 620 RecordDiscrepancyWithShill(network, status); 621 break; 622 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL: 623 if (network->connection_state() != shill::kStatePortal) 624 RecordDiscrepancyWithShill(network, status); 625 break; 626 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: 627 if (network->connection_state() != shill::kStateOnline) 628 RecordDiscrepancyWithShill(network, status); 629 break; 630 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT: 631 NOTREACHED(); 632 break; 633 } 634} 635 636void NetworkPortalDetectorImpl::ResetStrategyAndCounters() { 637 last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN; 638 same_detection_result_count_ = 0; 639 no_response_result_count_ = 0; 640 strategy_->Reset(); 641} 642 643} // namespace chromeos 644