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 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 bool network_changed = (default_service_path_ != default_network->path()); | 335 bool network_changed = (default_service_path_ != default_network->path()); |
330 default_service_path_ = default_network->path(); | 336 default_service_path_ = default_network->path(); |
331 | 337 |
332 bool connection_state_changed = | 338 bool connection_state_changed = |
333 (default_connection_state_ != default_network->connection_state()); | 339 (default_connection_state_ != default_network->connection_state()); |
334 default_connection_state_ = default_network->connection_state(); | 340 default_connection_state_ = default_network->connection_state(); |
335 | 341 |
336 if (network_changed || connection_state_changed) | 342 if (network_changed || connection_state_changed) |
337 StopDetection(); | 343 StopDetection(); |
338 | 344 |
339 if (CanPerformAttempt() && | 345 if (is_idle() && NetworkState::StateIsConnected(default_connection_state_)) { |
340 NetworkState::StateIsConnected(default_connection_state_)) { | |
341 // Initiate Captive Portal detection if network's captive | 346 // Initiate Captive Portal detection if network's captive |
342 // portal state is unknown (e.g. for freshly created networks), | 347 // portal state is unknown (e.g. for freshly created networks), |
343 // offline or if network connection state was changed. | 348 // offline or if network connection state was changed. |
344 CaptivePortalState state = GetCaptivePortalState(default_network->path()); | 349 CaptivePortalState state = GetCaptivePortalState(default_network->path()); |
345 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN || | 350 if (state.status == CAPTIVE_PORTAL_STATUS_UNKNOWN || |
346 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE || | 351 state.status == CAPTIVE_PORTAL_STATUS_OFFLINE || |
347 (!network_changed && connection_state_changed)) { | 352 (!network_changed && connection_state_changed)) { |
348 ScheduleAttempt(base::TimeDelta()); | 353 ScheduleAttempt(base::TimeDelta()); |
349 } | 354 } |
350 } | 355 } |
351 } | 356 } |
352 | 357 |
353 int NetworkPortalDetectorImpl::AttemptCount() { return attempt_count_; } | 358 uint64 NetworkPortalDetectorImpl::NoResponseResultCount() { |
| 359 return no_response_result_count_; |
| 360 } |
354 | 361 |
355 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() { | 362 base::TimeTicks NetworkPortalDetectorImpl::AttemptStartTime() { |
356 return attempt_start_time_; | 363 return attempt_start_time_; |
357 } | 364 } |
358 | 365 |
359 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() { | 366 base::TimeTicks NetworkPortalDetectorImpl::GetCurrentTimeTicks() { |
360 if (time_ticks_for_testing_.is_null()) | 367 if (time_ticks_for_testing_.is_null()) |
361 return base::TimeTicks::Now(); | 368 return base::TimeTicks::Now(); |
362 return time_ticks_for_testing_; | 369 return time_ticks_for_testing_; |
363 } | 370 } |
364 | 371 |
365 | 372 |
366 //////////////////////////////////////////////////////////////////////////////// | 373 //////////////////////////////////////////////////////////////////////////////// |
367 // NetworkPortalDetectorImpl, private: | 374 // NetworkPortalDetectorImpl, private: |
368 | 375 |
369 void NetworkPortalDetectorImpl::StartDetection() { | 376 void NetworkPortalDetectorImpl::StartDetection() { |
370 attempt_count_ = 0; | 377 DCHECK(is_idle()); |
371 DCHECK(CanPerformAttempt()); | 378 |
| 379 ResetStrategyAndCounters(); |
372 detection_start_time_ = GetCurrentTimeTicks(); | 380 detection_start_time_ = GetCurrentTimeTicks(); |
373 ScheduleAttempt(base::TimeDelta()); | 381 ScheduleAttempt(base::TimeDelta()); |
374 } | 382 } |
375 | 383 |
376 void NetworkPortalDetectorImpl::StopDetection() { | 384 void NetworkPortalDetectorImpl::StopDetection() { |
377 attempt_task_.Cancel(); | 385 attempt_task_.Cancel(); |
378 attempt_timeout_.Cancel(); | 386 attempt_timeout_.Cancel(); |
379 captive_portal_detector_->Cancel(); | 387 captive_portal_detector_->Cancel(); |
380 state_ = STATE_IDLE; | 388 state_ = STATE_IDLE; |
381 attempt_count_ = 0; | 389 ResetStrategyAndCounters(); |
382 } | |
383 | |
384 bool NetworkPortalDetectorImpl::CanPerformAttempt() const { | |
385 return is_idle() && strategy_->CanPerformAttempt(); | |
386 } | 390 } |
387 | 391 |
388 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) { | 392 void NetworkPortalDetectorImpl::ScheduleAttempt(const base::TimeDelta& delay) { |
389 DCHECK(CanPerformAttempt()); | 393 DCHECK(is_idle()); |
390 | 394 |
391 if (!IsEnabled()) | 395 if (!IsEnabled()) |
392 return; | 396 return; |
393 | 397 |
394 attempt_task_.Cancel(); | 398 attempt_task_.Cancel(); |
395 attempt_timeout_.Cancel(); | 399 attempt_timeout_.Cancel(); |
396 state_ = STATE_PORTAL_CHECK_PENDING; | 400 state_ = STATE_PORTAL_CHECK_PENDING; |
397 | 401 |
398 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt()); | 402 next_attempt_delay_ = std::max(delay, strategy_->GetDelayTillNextAttempt()); |
399 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt, | 403 attempt_task_.Reset(base::Bind(&NetworkPortalDetectorImpl::StartAttempt, |
(...skipping 30 matching lines...) Expand all Loading... |
430 << "id=" << default_network_id_; | 434 << "id=" << default_network_id_; |
431 | 435 |
432 captive_portal_detector_->Cancel(); | 436 captive_portal_detector_->Cancel(); |
433 CaptivePortalDetector::Results results; | 437 CaptivePortalDetector::Results results; |
434 results.result = captive_portal::RESULT_NO_RESPONSE; | 438 results.result = captive_portal::RESULT_NO_RESPONSE; |
435 OnAttemptCompleted(results); | 439 OnAttemptCompleted(results); |
436 } | 440 } |
437 | 441 |
438 void NetworkPortalDetectorImpl::OnAttemptCompleted( | 442 void NetworkPortalDetectorImpl::OnAttemptCompleted( |
439 const CaptivePortalDetector::Results& results) { | 443 const CaptivePortalDetector::Results& results) { |
| 444 DCHECK(CalledOnValidThread()); |
| 445 DCHECK(is_checking_for_portal()); |
| 446 |
440 captive_portal::CaptivePortalResult result = results.result; | 447 captive_portal::CaptivePortalResult result = results.result; |
441 int response_code = results.response_code; | 448 int response_code = results.response_code; |
442 | 449 |
443 DCHECK(CalledOnValidThread()); | |
444 DCHECK(is_checking_for_portal()); | |
445 | |
446 DetectionAttemptCompletedReport attempt_completed_report( | |
447 default_network_name_, | |
448 default_network_id_, | |
449 results.result, | |
450 results.response_code); | |
451 if (!attempt_completed_report_.Equals(attempt_completed_report)) { | |
452 attempt_completed_report_ = attempt_completed_report; | |
453 attempt_completed_report_.Report(); | |
454 } | |
455 | |
456 state_ = STATE_IDLE; | |
457 attempt_timeout_.Cancel(); | |
458 ++attempt_count_; | |
459 | |
460 const NetworkState* network = DefaultNetwork(); | 450 const NetworkState* network = DefaultNetwork(); |
461 | 451 |
462 // If using a fake profile client, also fake being behind a captive portal | 452 // If using a fake profile client, also fake being behind a captive portal |
463 // if the default network is in portal state. | 453 // if the default network is in portal state. |
464 if (result != captive_portal::RESULT_NO_RESPONSE && | 454 if (result != captive_portal::RESULT_NO_RESPONSE && |
465 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() && | 455 DBusThreadManager::Get()->GetShillProfileClient()->GetTestInterface() && |
466 network && network->connection_state() == shill::kStatePortal) { | 456 network && network->connection_state() == shill::kStatePortal) { |
467 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL; | 457 result = captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL; |
468 response_code = 200; | 458 response_code = 200; |
469 } | 459 } |
470 | 460 |
| 461 DetectionAttemptCompletedReport attempt_completed_report( |
| 462 default_network_name_, default_network_id_, result, response_code); |
| 463 if (!attempt_completed_report_.Equals(attempt_completed_report)) { |
| 464 attempt_completed_report_ = attempt_completed_report; |
| 465 attempt_completed_report_.Report(); |
| 466 } |
| 467 |
| 468 state_ = STATE_IDLE; |
| 469 attempt_timeout_.Cancel(); |
| 470 |
471 CaptivePortalState state; | 471 CaptivePortalState state; |
472 state.response_code = response_code; | 472 state.response_code = response_code; |
473 state.time = GetCurrentTimeTicks(); | 473 state.time = GetCurrentTimeTicks(); |
474 switch (result) { | 474 switch (result) { |
475 case captive_portal::RESULT_NO_RESPONSE: | 475 case captive_portal::RESULT_NO_RESPONSE: |
476 if (CanPerformAttempt()) { | 476 if (state.response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED) { |
477 ScheduleAttempt(results.retry_after_delta); | |
478 return; | |
479 } else if (state.response_code == | |
480 net::HTTP_PROXY_AUTHENTICATION_REQUIRED) { | |
481 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED; | 477 state.status = CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED; |
482 } else if (network && | 478 } else if (network && |
483 (network->connection_state() == shill::kStatePortal)) { | 479 (network->connection_state() == shill::kStatePortal)) { |
484 // Take into account shill's detection results. | 480 // Take into account shill's detection results. |
485 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; | 481 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; |
486 LOG(WARNING) << "Network name=" << network->name() << ", " | |
487 << "id=" << network->guid() << " " | |
488 << "is marked as " | |
489 << CaptivePortalStatusString(state.status) << " " | |
490 << "despite the fact that CaptivePortalDetector " | |
491 << "received no response"; | |
492 } else { | 482 } else { |
493 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; | 483 state.status = CAPTIVE_PORTAL_STATUS_OFFLINE; |
494 } | 484 } |
495 break; | 485 break; |
496 case captive_portal::RESULT_INTERNET_CONNECTED: | 486 case captive_portal::RESULT_INTERNET_CONNECTED: |
497 state.status = CAPTIVE_PORTAL_STATUS_ONLINE; | 487 state.status = CAPTIVE_PORTAL_STATUS_ONLINE; |
498 break; | 488 break; |
499 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: | 489 case captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL: |
500 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; | 490 state.status = CAPTIVE_PORTAL_STATUS_PORTAL; |
501 break; | 491 break; |
502 default: | 492 default: |
503 break; | 493 break; |
504 } | 494 } |
505 | 495 |
506 OnDetectionCompleted(network, state); | 496 if (last_detection_result_ != state.status) { |
507 if (CanPerformAttempt() && strategy_->CanPerformAttemptAfterDetection()) | 497 last_detection_result_ = state.status; |
508 ScheduleAttempt(base::TimeDelta()); | 498 same_detection_result_count_ = 1; |
| 499 strategy_->Reset(); |
| 500 } else { |
| 501 ++same_detection_result_count_; |
| 502 } |
| 503 strategy_->OnDetectionCompleted(); |
| 504 |
| 505 if (result == captive_portal::RESULT_NO_RESPONSE) |
| 506 ++no_response_result_count_; |
| 507 else |
| 508 no_response_result_count_ = 0; |
| 509 |
| 510 if (state.status != CAPTIVE_PORTAL_STATUS_OFFLINE || |
| 511 same_detection_result_count_ >= |
| 512 static_cast<uint64>(kMaxOfflineResultsBeforeReport)) { |
| 513 OnDetectionCompleted(network, state); |
| 514 } |
| 515 ScheduleAttempt(results.retry_after_delta); |
509 } | 516 } |
510 | 517 |
511 void NetworkPortalDetectorImpl::Observe( | 518 void NetworkPortalDetectorImpl::Observe( |
512 int type, | 519 int type, |
513 const content::NotificationSource& source, | 520 const content::NotificationSource& source, |
514 const content::NotificationDetails& details) { | 521 const content::NotificationDetails& details) { |
515 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED || | 522 if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED || |
516 type == chrome::NOTIFICATION_AUTH_SUPPLIED || | 523 type == chrome::NOTIFICATION_AUTH_SUPPLIED || |
517 type == chrome::NOTIFICATION_AUTH_CANCELLED) { | 524 type == chrome::NOTIFICATION_AUTH_CANCELLED) { |
518 VLOG(1) << "Restarting portal detection due to proxy change."; | 525 VLOG(1) << "Restarting portal detection due to proxy change."; |
519 attempt_count_ = 0; | |
520 if (is_portal_check_pending()) | |
521 return; | |
522 StopDetection(); | 526 StopDetection(); |
523 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec)); | 527 ScheduleAttempt(base::TimeDelta::FromSeconds(kProxyChangeDelaySec)); |
524 } | 528 } |
525 } | 529 } |
526 | 530 |
527 void NetworkPortalDetectorImpl::OnDetectionCompleted( | 531 void NetworkPortalDetectorImpl::OnDetectionCompleted( |
528 const NetworkState* network, | 532 const NetworkState* network, |
529 const CaptivePortalState& state) { | 533 const CaptivePortalState& state) { |
530 if (!network) { | 534 if (!network) { |
531 NotifyDetectionCompleted(network, state); | 535 NotifyDetectionCompleted(network, state); |
532 return; | 536 return; |
533 } | 537 } |
534 | 538 |
535 CaptivePortalStateMap::const_iterator it = | 539 CaptivePortalStateMap::const_iterator it = |
536 portal_state_map_.find(network->path()); | 540 portal_state_map_.find(network->path()); |
537 if (it == portal_state_map_.end() || it->second.status != state.status || | 541 if (it == portal_state_map_.end() || it->second.status != state.status || |
538 it->second.response_code != state.response_code) { | 542 it->second.response_code != state.response_code) { |
539 VLOG(1) << "Updating Chrome Captive Portal state: " | |
540 << "name=" << network->name() << ", " | |
541 << "id=" << network->guid() << ", " | |
542 << "status=" << CaptivePortalStatusString(state.status) << ", " | |
543 << "response_code=" << state.response_code; | |
544 | |
545 // Record detection duration iff detection result differs from the | 543 // Record detection duration iff detection result differs from the |
546 // previous one for this network. The reason is to record all stats | 544 // previous one for this network. The reason is to record all stats |
547 // only when network changes it's state. | 545 // only when network changes it's state. |
548 RecordDetectionStats(network, state.status); | 546 RecordDetectionStats(network, state.status); |
549 if (it != portal_state_map_.end() && | 547 if (it != portal_state_map_.end() && |
550 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL && | 548 it->second.status == CAPTIVE_PORTAL_STATUS_PORTAL && |
551 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { | 549 state.status == CAPTIVE_PORTAL_STATUS_ONLINE) { |
552 RecordPortalToOnlineTransition(state.time - it->second.time); | 550 RecordPortalToOnlineTransition(state.time - it->second.time); |
553 } | 551 } |
554 | 552 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
601 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: | 599 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED: |
602 if (network->connection_state() != shill::kStateOnline) | 600 if (network->connection_state() != shill::kStateOnline) |
603 RecordDiscrepancyWithShill(network, status); | 601 RecordDiscrepancyWithShill(network, status); |
604 break; | 602 break; |
605 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT: | 603 case NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_COUNT: |
606 NOTREACHED(); | 604 NOTREACHED(); |
607 break; | 605 break; |
608 } | 606 } |
609 } | 607 } |
610 | 608 |
| 609 void NetworkPortalDetectorImpl::ResetStrategyAndCounters() { |
| 610 last_detection_result_ = CAPTIVE_PORTAL_STATUS_UNKNOWN; |
| 611 same_detection_result_count_ = 0; |
| 612 no_response_result_count_ = 0; |
| 613 strategy_->Reset(); |
| 614 } |
| 615 |
611 } // namespace chromeos | 616 } // namespace chromeos |
OLD | NEW |