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 |