| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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_impl.h" | 5 #include "chrome/browser/chromeos/net/network_portal_detector_impl.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 | 26 |
| 27 using captive_portal::CaptivePortalDetector; | 27 using captive_portal::CaptivePortalDetector; |
| 28 | 28 |
| 29 namespace chromeos { | 29 namespace chromeos { |
| 30 | 30 |
| 31 namespace { | 31 namespace { |
| 32 | 32 |
| 33 // Delay before portal detection caused by changes in proxy settings. | 33 // Delay before portal detection caused by changes in proxy settings. |
| 34 const int kProxyChangeDelaySec = 1; | 34 const int kProxyChangeDelaySec = 1; |
| 35 | 35 |
| 36 // Maximum number of reports from captive portal detector about |
| 37 // offline state in a row before notification is sent to observers. |
| 38 const int kMaxOfflineResultsBeforeReport = 3; |
| 39 |
| 36 const NetworkState* DefaultNetwork() { | 40 const NetworkState* DefaultNetwork() { |
| 37 return NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); | 41 return NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); |
| 38 } | 42 } |
| 39 | 43 |
| 40 bool InSession() { | 44 bool InSession() { |
| 41 return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn(); | 45 return LoginState::IsInitialized() && LoginState::Get()->IsUserLoggedIn(); |
| 42 } | 46 } |
| 43 | 47 |
| 44 void RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) { | 48 void RecordDetectionResult(NetworkPortalDetector::CaptivePortalStatus status) { |
| 45 if (InSession()) { | 49 if (InSession()) { |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 195 set_network_portal_detector(new NetworkPortalDetectorStubImpl()); | 199 set_network_portal_detector(new NetworkPortalDetectorStubImpl()); |
| 196 else | 200 else |
| 197 set_network_portal_detector(new NetworkPortalDetectorImpl(url_context)); | 201 set_network_portal_detector(new NetworkPortalDetectorImpl(url_context)); |
| 198 } | 202 } |
| 199 | 203 |
| 200 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl( | 204 NetworkPortalDetectorImpl::NetworkPortalDetectorImpl( |
| 201 const scoped_refptr<net::URLRequestContextGetter>& request_context) | 205 const scoped_refptr<net::URLRequestContextGetter>& request_context) |
| 202 : state_(STATE_IDLE), | 206 : state_(STATE_IDLE), |
| 203 test_url_(CaptivePortalDetector::kDefaultURL), | 207 test_url_(CaptivePortalDetector::kDefaultURL), |
| 204 enabled_(false), | 208 enabled_(false), |
| 205 attempt_count_(0), | |
| 206 strategy_(PortalDetectorStrategy::CreateById( | 209 strategy_(PortalDetectorStrategy::CreateById( |
| 207 PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN)), | 210 PortalDetectorStrategy::STRATEGY_ID_LOGIN_SCREEN)), |
| 211 last_detection_result_(CAPTIVE_PORTAL_STATUS_UNKNOWN), |
| 212 same_detection_result_count_(0), |
| 213 no_response_result_count_(0), |
| 208 weak_factory_(this) { | 214 weak_factory_(this) { |
| 209 captive_portal_detector_.reset(new CaptivePortalDetector(request_context)); | 215 captive_portal_detector_.reset(new CaptivePortalDetector(request_context)); |
| 210 strategy_->set_delegate(this); | 216 strategy_->set_delegate(this); |
| 211 | 217 |
| 212 registrar_.Add(this, | 218 registrar_.Add(this, |
| 213 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED, | 219 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED, |
| 214 content::NotificationService::AllSources()); | 220 content::NotificationService::AllSources()); |
| 215 registrar_.Add(this, | 221 registrar_.Add(this, |
| 216 chrome::NOTIFICATION_AUTH_SUPPLIED, | 222 chrome::NOTIFICATION_AUTH_SUPPLIED, |
| 217 content::NotificationService::AllSources()); | 223 content::NotificationService::AllSources()); |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 VLOG(1) << "DefaultNetworkChanged: " | 343 VLOG(1) << "DefaultNetworkChanged: " |
| 338 << "name=" << default_network_name_ << ", " | 344 << "name=" << default_network_name_ << ", " |
| 339 << "id=" << default_network_id_ << ", " | 345 << "id=" << default_network_id_ << ", " |
| 340 << "state=" << default_connection_state_ << ", " | 346 << "state=" << default_connection_state_ << ", " |
| 341 << "changed=" << network_changed << ", " | 347 << "changed=" << network_changed << ", " |
| 342 << "state_changed=" << connection_state_changed; | 348 << "state_changed=" << connection_state_changed; |
| 343 | 349 |
| 344 if (network_changed || connection_state_changed) | 350 if (network_changed || connection_state_changed) |
| 345 StopDetection(); | 351 StopDetection(); |
| 346 | 352 |
| 347 if (CanPerformAttempt() && | 353 if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) { |
| 348 NetworkState::StateIsConnected(default_connection_state_)) { | |
| 349 // Initiate Captive Portal detection if network's captive | 354 // Initiate Captive Portal detection if network's captive |
| 350 // portal state is unknown (e.g. for freshly created networks), | 355 // portal state is unknown (e.g. for freshly created networks), |
| 351 // offline or if network connection state was changed. | 356 // offline or if network connection state was changed. |
| 352 CaptivePortalState state = GetCaptivePortalState(default_network->guid()); | 357 CaptivePortalState state = GetCaptivePortalState(default_network->guid()); |
| 353 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN || | 358 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN || |
| 354 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE || | 359 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE || |
| 355 (!network_changed && connection_state_changed)) { | 360 (!network_changed && connection_state_changed)) { |
| 356 ScheduleAttempt(base::TimeDelta()); | 361 ScheduleAttempt(base::TimeDelta()); |
| 357 } | 362 } |
| 358 } | 363 } |
| 359 } | 364 } |
| 360 | 365 |
| 361 int NetworkPortalDetectorImpl::AttemptCount() { return attempt_count_; } | 366 int NetworkPortalDetectorImpl::NoResponseResultCount() { |
| 367 return no_response_result_count_; |
| 368 } |
| 362 | 369 |
| 363 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() { | 370 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() { |
| 364 return attempt_start_time_; | 371 return attempt_start_time_; |
| 365 } | 372 } |
| 366 | 373 |
| 367 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() { | 374 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() { |
| 368 if (time_ticks_for_testing_.is_null()) | 375 if (time_ticks_for_testing_.is_null()) |
| 369 return base::TimeTicks::Now(); | 376 return base::TimeTicks::Now(); |
| 370 return time_ticks_for_testing_; | 377 return time_ticks_for_testing_; |
| 371 } | 378 } |
| 372 | 379 |
| 373 | 380 |
| 374 //////////////////////////////////////////////////////////////////////////////// | 381 //////////////////////////////////////////////////////////////////////////////// |
| 375 // NetworkPortalDetectorImpl, private: | 382 // NetworkPortalDetectorImpl, private: |
| 376 | 383 |
| 377 void NetworkPortalDetectorImpl::StartDetection() { | 384 void NetworkPortalDetectorImpl::StartDetection() { |
| 378 attempt_count_ = 0; | 385 DCHECK(is_idle()); |
| 379 DCHECK(CanPerformAttempt()); | 386 |
| 387 ResetStrategyAndCounters(); |
| 380 detection_start_time_ = GetCurrentTimeTicks(); | 388 detection_start_time_ = GetCurrentTimeTicks(); |
| 381 ScheduleAttempt(base::TimeDelta()); | 389 ScheduleAttempt(base::TimeDelta()); |
| 382 } | 390 } |
| 383 | 391 |
| 384 void NetworkPortalDetectorImpl::StopDetection() { | 392 void NetworkPortalDetectorImpl::StopDetection() { |
| 385 attempt_task_.Cancel(); | 393 attempt_task_.Cancel(); |
| 386 attempt_timeout_.Cancel(); | 394 attempt_timeout_.Cancel(); |
| 387 captive_portal_detector_->Cancel(); | 395 captive_portal_detector_->Cancel(); |
| 388 state_ = STATE_IDLE; | 396 state_ = STATE_IDLE; |
| 389 attempt_count_ = 0; | 397 ResetStrategyAndCounters(); |
| 390 } | |
| 391 | |
| 392 bool NetworkPortalDetectorImpl::CanPerformAttempt() const { | |
| 393 return is_idle() && strategy_->CanPerformAttempt(); | |
| 394 } | 398 } |
| 395 | 399 |
| 396 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) { | 400 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) { |
| 397 DCHECK(CanPerformAttempt()); | 401 DCHECK(is_idle()); |
| 398 | 402 |
| 399 if (!IsEnabled()) | 403 if (!IsEnabled()) |
| 400 return; | 404 return; |
| 401 | 405 |
| 402 attempt_task_.Cancel(); | 406 attempt_task_.Cancel(); |
| 403 attempt_timeout_.Cancel(); | 407 attempt_timeout_.Cancel(); |
| 404 state_ = STATE_PORTAL_CHECK_PENDING; | 408 state_ = STATE_PORTAL_CHECK_PENDING; |
| 405 | 409 |
| 406 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt()); | 410 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt()); |
| 407 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt, | 411 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt, |
| (...skipping 30 matching lines...) Expand all Loading... |
| 438 << "id=" << default_network_id_; | 442 << "id=" << default_network_id_; |
| 439 | 443 |
| 440 captive_portal_detector_->Cancel(); | 444 captive_portal_detector_->Cancel(); |
| 441 CaptivePortalDetector::Results results; | 445 CaptivePortalDetector::Results results; |
| 442 results.result = captive_portal::RESULT_NO_RESPONSE; | 446 results.result = captive_portal::RESULT_NO_RESPONSE; |
| 443 OnAttemptCompleted(results); | 447 OnAttemptCompleted(results); |
| 444 } | 448 } |
| 445 | 449 |
| 446 void NetworkPortalDetectorImpl::OnAttemptCompleted( | 450 void NetworkPortalDetectorImpl::OnAttemptCompleted( |
| 447 const CaptivePortalDetector::Results& results) { | 451 const CaptivePortalDetector::Results& results) { |
| 452 DCHECK(CalledOnValidThread()); |
| 453 DCHECK(is_checking_for_portal()); |
| 454 |
| 448 captive_portal::CaptivePortalResult result = results.result; | 455 captive_portal::CaptivePortalResult result = results.result; |
| 449 int response_code = results.response_code; | 456 int response_code = results.response_code; |
| 450 | 457 |
| 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(); | 458 const NetworkState* network = DefaultNetwork(); |
| 469 | 459 |
| 470 // If using a fake profile client, also fake being behind a captive portal | 460 // If using a fake profile client, also fake being behind a captive portal |
| 471 // if the default network is in portal state. | 461 // if the default network is in portal state. |
| 472 if (result != captive_portal::RESULT_NO_RESPONSE && | 462 if (result != captive_portal::RESULT_NO_RESPONSE && |
| 473 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() && | 463 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() && |
| 474 network && network->connection_state() == shill::kStatePortal) { | 464 network && network->connection_state() == shill::kStatePortal) { |
| 475 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL; | 465 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL; |
| 476 response_code = 200; | 466 response_code = 200; |
| 477 } | 467 } |
| 478 | 468 |
| 469 DetectionAttemptCompletedReport attempt_completed_report( |
| 470 default_network_name_, default_network_id_, result, response_code); |
| 471 if (!attempt_completed_report_.Equals(attempt_completed_report)) { |
| 472 attempt_completed_report_ = attempt_completed_report; |
| 473 attempt_completed_report_.Report(); |
| 474 } |
| 475 |
| 476 state_ = STATE_IDLE; |
| 477 attempt_timeout_.Cancel(); |
| 478 |
| 479 CaptivePortalState state; | 479 CaptivePortalState state; |
| 480 state.response_code = response_code; | 480 state.response_code = response_code; |
| 481 state.time = GetCurrentTimeTicks(); | 481 state.time = GetCurrentTimeTicks(); |
| 482 switch (result) { | 482 switch (result) { |
| 483 case captive_portal::RESULT_NO_RESPONSE: | 483 case captive_portal::RESULT_NO_RESPONSE: |
| 484 if (CanPerformAttempt()) { | 484 if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) { |
| 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; | 485 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED; |
| 490 } else if (network && | 486 } else if (network && |
| 491 (network->connection_state() == shill::kStatePortal)) { | 487 (network->connection_state() == shill::kStatePortal)) { |
| 492 // Take into account shill's detection results. | 488 // Take into account shill's detection results. |
| 493 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; | 489 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 { | 490 } else { |
| 501 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; | 491 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; |
| 502 } | 492 } |
| 503 break; | 493 break; |
| 504 case captive_portal::RESULT_INTERNET_CONNECTED: | 494 case captive_portal::RESULT_INTERNET_CONNECTED: |
| 505 state.status = CAPTIVE_PORTAL_STATUS_ONLINE; | 495 state.status = CAPTIVE_PORTAL_STATUS_ONLINE; |
| 506 break; | 496 break; |
| 507 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: | 497 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: |
| 508 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; | 498 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; |
| 509 break; | 499 break; |
| 510 default: | 500 default: |
| 511 break; | 501 break; |
| 512 } | 502 } |
| 513 | 503 |
| 514 OnDetectionCompleted(network, state); | 504 if (last_detection_result_ != state.status) { |
| 515 if (CanPerformAttempt() && strategy_->CanPerformAttemptAfterDetection()) | 505 last_detection_result_ = state.status; |
| 516 ScheduleAttempt(base::TimeDelta()); | 506 same_detection_result_count_ = 1; |
| 507 strategy_->Reset(); |
| 508 } else { |
| 509 ++same_detection_result_count_; |
| 510 } |
| 511 strategy_->OnDetectionCompleted(); |
| 512 |
| 513 if (result == captive_portal::RESULT_NO_RESPONSE) |
| 514 ++no_response_result_count_; |
| 515 else |
| 516 no_response_result_count_ = 0; |
| 517 |
| 518 if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE || |
| 519 same_detection_result_count_ >= kMaxOfflineResultsBeforeReport) { |
| 520 OnDetectionCompleted(network, state); |
| 521 } |
| 522 ScheduleAttempt(results.retry_after_delta); |
| 517 } | 523 } |
| 518 | 524 |
| 519 void NetworkPortalDetectorImpl::Observe( | 525 void NetworkPortalDetectorImpl::Observe( |
| 520 int type, | 526 int type, |
| 521 const content::NotificationSource& source, | 527 const content::NotificationSource& source, |
| 522 const content::NotificationDetails& details) { | 528 const content::NotificationDetails& details) { |
| 523 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED || | 529 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED || |
| 524 type == chrome::NOTIFICATION_AUTH_SUPPLIED || | 530 type == chrome::NOTIFICATION_AUTH_SUPPLIED || |
| 525 type == chrome::NOTIFICATION_AUTH_CANCELLED) { | 531 type == chrome::NOTIFICATION_AUTH_CANCELLED) { |
| 526 VLOG(1) << "Restarting portal detection due to proxy change."; | 532 VLOG(1) << "Restarting portal detection due to proxy change."; |
| 527 attempt_count_ = 0; | |
| 528 if (is_portal_check_pending()) | |
| 529 return; | |
| 530 StopDetection(); | 533 StopDetection(); |
| 531 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec)); | 534 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec)); |
| 532 } | 535 } |
| 533 } | 536 } |
| 534 | 537 |
| 535 void NetworkPortalDetectorImpl::OnDetectionCompleted( | 538 void NetworkPortalDetectorImpl::OnDetectionCompleted( |
| 536 const NetworkState* network, | 539 const NetworkState* network, |
| 537 const CaptivePortalState& state) { | 540 const CaptivePortalState& state) { |
| 538 if (!network) { | 541 if (!network) { |
| 539 NotifyDetectionCompleted(network, state); | 542 NotifyDetectionCompleted(network, state); |
| 540 return; | 543 return; |
| 541 } | 544 } |
| 542 | 545 |
| 543 CaptivePortalStateMap::const_iterator it = | 546 CaptivePortalStateMap::const_iterator it = |
| 544 portal_state_map_.find(network->guid()); | 547 portal_state_map_.find(network->guid()); |
| 545 if (it == portal_state_map_.end() || it->second.status != state.status || | 548 if (it == portal_state_map_.end() || it->second.status != state.status || |
| 546 it->second.response_code != state.response_code) { | 549 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 | 550 // Record detection duration iff detection result differs from the |
| 554 // previous one for this network. The reason is to record all stats | 551 // previous one for this network. The reason is to record all stats |
| 555 // only when network changes it's state. | 552 // only when network changes it's state. |
| 556 RecordDetectionStats(network, state.status); | 553 RecordDetectionStats(network, state.status); |
| 557 if (it != portal_state_map_.end() && | 554 if (it != portal_state_map_.end() && |
| 558 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL && | 555 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL && |
| 559 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { | 556 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { |
| 560 RecordPortalToOnlineTransition(state.time - it->second.time); | 557 RecordPortalToOnlineTransition(state.time - it->second.time); |
| 561 } | 558 } |
| 562 | 559 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 609 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: | 606 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: |
| 610 if (network->connection_state() != shill::kStateOnline) | 607 if (network->connection_state() != shill::kStateOnline) |
| 611 RecordDiscrepancyWithShill(network, status); | 608 RecordDiscrepancyWithShill(network, status); |
| 612 break; | 609 break; |
| 613 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT: | 610 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT: |
| 614 NOTREACHED(); | 611 NOTREACHED(); |
| 615 break; | 612 break; |
| 616 } | 613 } |
| 617 } | 614 } |
| 618 | 615 |
| 616 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() { |
| 617 last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN; |
| 618 same_detection_result_count_ = 0; |
| 619 no_response_result_count_ = 0; |
| 620 strategy_->Reset(); |
| 621 } |
| 622 |
| 619 } // namespace chromeos | 623 } // namespace chromeos |
| OLD | NEW |