Index: chrome/browser/chromeos/net/network_portal_detector.cc |
diff --git a/chrome/browser/chromeos/net/network_portal_detector.cc b/chrome/browser/chromeos/net/network_portal_detector.cc |
index a70418bbfd120d80ea06ce1abe875c3fe0e5e9f9..9e525764a868eda073cdaa01cc337119c5ad94db 100644 |
--- a/chrome/browser/chromeos/net/network_portal_detector.cc |
+++ b/chrome/browser/chromeos/net/network_portal_detector.cc |
@@ -7,6 +7,7 @@ |
#include "base/bind.h" |
#include "base/command_line.h" |
#include "base/logging.h" |
+#include "base/message_loop.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/chromeos/cros/cros_library.h" |
#include "chrome/common/chrome_switches.h" |
@@ -20,6 +21,17 @@ namespace chromeos { |
namespace { |
+// Maximum number of portal detections for the same active network |
+// after network change. |
+const int kMaxRequestAttempts = 3; |
+ |
+// Minimum timeout between consecutive portal checks for the same |
+// network. |
+const int kMinTimeBetweenAttemptsSec = 3; |
+ |
+// Timeout for a portal check. |
+const int kRequestTimeoutSec = 10; |
+ |
std::string CaptivePortalStateString( |
NetworkPortalDetector::CaptivePortalState state) { |
switch (state) { |
@@ -44,7 +56,12 @@ NetworkPortalDetector* g_network_portal_detector = NULL; |
NetworkPortalDetector::NetworkPortalDetector( |
const scoped_refptr<net::URLRequestContextGetter>& request_context) |
- : test_url_(CaptivePortalDetector::kDefaultURL) { |
+ : test_url_(CaptivePortalDetector::kDefaultURL), |
+ weak_ptr_factory_(this), |
+ attempt_count_(0), |
+ min_time_between_attempts_( |
+ base::TimeDelta::FromSeconds(kMinTimeBetweenAttemptsSec)), |
+ request_timeout_(base::TimeDelta::FromSeconds(kRequestTimeoutSec)) { |
captive_portal_detector_.reset( |
new CaptivePortalDetector(request_context)); |
} |
@@ -64,6 +81,9 @@ void NetworkPortalDetector::Init() { |
void NetworkPortalDetector::Shutdown() { |
DCHECK(CalledOnValidThread()); |
+ detection_task_.Cancel(); |
+ detection_timeout_.Cancel(); |
+ |
captive_portal_detector_->Cancel(); |
captive_portal_detector_.reset(); |
observers_.Clear(); |
@@ -74,17 +94,21 @@ void NetworkPortalDetector::Shutdown() { |
void NetworkPortalDetector::AddObserver(Observer* observer) { |
DCHECK(CalledOnValidThread()); |
+ |
if (!observers_.HasObserver(observer)) |
observers_.AddObserver(observer); |
} |
void NetworkPortalDetector::RemoveObserver(Observer* observer) { |
DCHECK(CalledOnValidThread()); |
+ |
observers_.RemoveObserver(observer); |
} |
NetworkPortalDetector::CaptivePortalState |
NetworkPortalDetector::GetCaptivePortalState(const Network* network) { |
+ DCHECK(CalledOnValidThread()); |
+ |
if (!network) |
return CAPTIVE_PORTAL_STATE_UNKNOWN; |
CaptivePortalStateMap::const_iterator it = |
@@ -109,22 +133,27 @@ void NetworkPortalDetector::OnNetworkManagerChanged(NetworkLibrary* cros) { |
if (active_network_id_ != new_active_network_id) { |
active_network_id_ = new_active_network_id; |
- if (IsCheckingForPortal()) { |
- // Network is changed, so detection results will be incorrect. |
- state_ = STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED; |
- } else if (Network::IsConnectedState(connection_state)) { |
- // Start captive portal detection, if possible. |
- DetectCaptivePortal(); |
+ attempt_count_ = 0; |
+ if (IsPortalCheckPending()) { |
+ detection_task_.Cancel(); |
+ detection_timeout_.Cancel(); |
+ } else if (IsCheckingForPortal()) { |
+ captive_portal_detector_->Cancel(); |
} |
- } else if (!IsCheckingForPortal() && |
- Network::IsConnectedState(connection_state)) { |
+ state_ = STATE_IDLE; |
+ } |
+ if (!IsCheckingForPortal() && !IsPortalCheckPending() && |
+ Network::IsConnectedState(connection_state) && |
+ attempt_count_ < kMaxRequestAttempts) { |
DCHECK(active_network); |
+ |
// Initiate Captive Portal detection only if network's captive |
- // portal state is unknown (e.g. for freshly created networks) or offline. |
+ // portal state is unknown (e.g. for freshly created networks) or |
+ // offline. |
CaptivePortalState state = GetCaptivePortalState(active_network); |
if (state == CAPTIVE_PORTAL_STATE_UNKNOWN || |
state == CAPTIVE_PORTAL_STATE_OFFLINE) { |
- DetectCaptivePortal(); |
+ DetectCaptivePortal(base::TimeDelta()); |
} |
} |
} |
@@ -150,41 +179,94 @@ bool NetworkPortalDetector::IsEnabled() { |
switches::kDisableChromeCaptivePortalDetector); |
} |
-void NetworkPortalDetector::DetectCaptivePortal() { |
+void NetworkPortalDetector::DetectCaptivePortal(const base::TimeDelta& delay) { |
+ DCHECK(!IsPortalCheckPending()); |
+ DCHECK(!IsCheckingForPortal()); |
+ DCHECK(attempt_count_ < kMaxRequestAttempts); |
+ |
+ detection_task_.Cancel(); |
+ detection_timeout_.Cancel(); |
+ state_ = STATE_PORTAL_CHECK_PENDING; |
+ |
+ next_attempt_delay_ = delay; |
+ if (attempt_count_ > 0) { |
+ base::TimeTicks now = GetCurrentTimeTicks(); |
+ base::TimeDelta elapsed_time; |
+ if (now > attempt_start_time_) |
+ elapsed_time = now - attempt_start_time_; |
+ if (elapsed_time < min_time_between_attempts_ && |
+ min_time_between_attempts_ - elapsed_time > next_attempt_delay_) { |
+ next_attempt_delay_ = min_time_between_attempts_ - elapsed_time; |
+ } |
+ } |
+ detection_task_.Reset( |
+ base::Bind(&NetworkPortalDetector::DetectCaptivePortalTask, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ detection_task_.callback(), |
+ next_attempt_delay_); |
+} |
+ |
+void NetworkPortalDetector::DetectCaptivePortalTask() { |
+ DCHECK(IsPortalCheckPending()); |
+ |
state_ = STATE_CHECKING_FOR_PORTAL; |
+ |
+ ++attempt_count_; |
+ attempt_start_time_ = GetCurrentTimeTicks(); |
+ |
+ VLOG(1) << "Portal detection started: " |
+ << "network=" << active_network_id_ << ", " |
+ << "attempt=" << attempt_count_ << " of " << kMaxRequestAttempts; |
+ |
captive_portal_detector_->DetectCaptivePortal( |
test_url_, |
base::Bind(&NetworkPortalDetector::OnPortalDetectionCompleted, |
- base::Unretained(this))); |
+ weak_ptr_factory_.GetWeakPtr())); |
+ detection_timeout_.Reset( |
+ base::Bind(&NetworkPortalDetector::PortalDetectionTimeout, |
+ weak_ptr_factory_.GetWeakPtr())); |
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, |
+ detection_timeout_.callback(), |
+ request_timeout_); |
+} |
+ |
+void NetworkPortalDetector::PortalDetectionTimeout() { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(IsCheckingForPortal()); |
+ |
+ VLOG(1) << "Portal detection timeout: network=" << active_network_id_; |
+ |
+ captive_portal_detector_->Cancel(); |
+ CaptivePortalDetector::Results results; |
+ results.result = captive_portal::RESULT_NO_RESPONSE; |
+ OnPortalDetectionCompleted(results); |
} |
void NetworkPortalDetector::OnPortalDetectionCompleted( |
const CaptivePortalDetector::Results& results) { |
DCHECK(CalledOnValidThread()); |
- |
DCHECK(IsCheckingForPortal()); |
+ VLOG(1) << "Portal detection completed: " |
+ << "network=" << active_network_id_ << ", " |
+ << "result=" << CaptivePortalDetector::CaptivePortalResultToString( |
+ results.result); |
+ |
+ state_ = STATE_IDLE; |
+ detection_timeout_.Cancel(); |
+ |
NetworkLibrary* cros = CrosLibrary::Get()->GetNetworkLibrary(); |
const Network* active_network = cros->active_network(); |
if (!active_network) |
return; |
- if (state_ == STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED) { |
- // Can't use detection results, because network was changed. So |
- // retry portal detection for the active network or, if possible. |
- if (Network::IsConnectedState(active_network->connection_state()) && |
- GetCaptivePortalState(active_network) == |
- CAPTIVE_PORTAL_STATE_UNKNOWN) { |
- DetectCaptivePortal(); |
- } |
- return; |
- } |
- |
- state_ = STATE_IDLE; |
- |
switch (results.result) { |
case captive_portal::RESULT_NO_RESPONSE: |
- SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_OFFLINE); |
+ if (attempt_count_ >= kMaxRequestAttempts) |
+ SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_OFFLINE); |
+ else |
+ DetectCaptivePortal(results.retry_after_delta); |
break; |
case captive_portal::RESULT_INTERNET_CONNECTED: |
SetCaptivePortalState(active_network, CAPTIVE_PORTAL_STATE_ONLINE); |
@@ -197,19 +279,23 @@ void NetworkPortalDetector::OnPortalDetectionCompleted( |
} |
} |
+bool NetworkPortalDetector::IsPortalCheckPending() const { |
+ return state_ == STATE_PORTAL_CHECK_PENDING; |
+} |
+ |
bool NetworkPortalDetector::IsCheckingForPortal() const { |
- return state_ == STATE_CHECKING_FOR_PORTAL || |
- state_ == STATE_CHECKING_FOR_PORTAL_NETWORK_CHANGED; |
+ return state_ == STATE_CHECKING_FOR_PORTAL; |
} |
void NetworkPortalDetector::SetCaptivePortalState(const Network* network, |
CaptivePortalState state) { |
DCHECK(network); |
+ |
CaptivePortalStateMap::const_iterator it = |
captive_portal_state_map_.find(network->unique_id()); |
if (it == captive_portal_state_map_.end() || |
it->second != state) { |
- VLOG(2) << "Updating Chrome Captive Portal state: " |
+ VLOG(1) << "Updating Chrome Captive Portal state: " |
<< "network=" << network->unique_id() << ", " |
<< "state=" << CaptivePortalStateString(state); |
captive_portal_state_map_[network->unique_id()] = state; |
@@ -222,4 +308,11 @@ void NetworkPortalDetector::NotifyPortalStateChanged(const Network* network, |
FOR_EACH_OBSERVER(Observer, observers_, OnPortalStateChanged(network, state)); |
} |
+base::TimeTicks NetworkPortalDetector::GetCurrentTimeTicks() const { |
+ if (time_ticks_for_testing_.is_null()) |
+ return base::TimeTicks::Now(); |
+ else |
+ return time_ticks_for_testing_; |
+} |
+ |
} // namespace chromeos |