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