This source file includes following definitions.
- RecordRepeatHistograms
- GetHistogramEntryForDetectionResult
- ShouldDeferToNativeCaptivePortalDetection
- captive_portal_service_
- ImplGetTimeNow
- initial_backoff_portal_ms
- test_url_
- DetectCaptivePortal
- DetectCaptivePortalInternal
- OnPortalDetectionCompleted
- Shutdown
- OnResult
- ResetBackoffEntry
- UpdateEnabledState
- GetCurrentTimeTicks
- DetectionInProgress
- TimerRunning
#include "chrome/browser/captive_portal/captive_portal_service.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#endif
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif
namespace captive_portal {
namespace {
enum CaptivePortalDetectionResult {
DETECTION_RESULT_INTERNET_CONNECTED,
DETECTION_RESULT_NO_RESPONSE,
DETECTION_RESULT_BEHIND_CAPTIVE_PORTAL,
DETECTION_RESULT_NO_RESPONSE_HTTPS_LANDING_URL,
DETECTION_RESULT_BEHIND_CAPTIVE_PORTAL_HTTPS_LANDING_URL,
DETECTION_RESULT_COUNT
};
void RecordRepeatHistograms(Result result,
int repeat_count,
base::TimeDelta result_duration) {
base::HistogramBase* result_repeated_histogram =
base::Histogram::FactoryGet(
"CaptivePortal.ResultRepeated." +
CaptivePortalDetector::CaptivePortalResultToString(result),
1,
100,
100,
base::Histogram::kUmaTargetedHistogramFlag);
result_repeated_histogram->Add(repeat_count);
if (repeat_count == 0)
return;
base::HistogramBase* result_duration_histogram =
base::Histogram::FactoryTimeGet(
"CaptivePortal.ResultDuration." +
CaptivePortalDetector::CaptivePortalResultToString(result),
base::TimeDelta::FromSeconds(1),
base::TimeDelta::FromHours(1),
50,
base::Histogram::kUmaTargetedHistogramFlag);
result_duration_histogram->AddTime(result_duration);
}
int GetHistogramEntryForDetectionResult(
const CaptivePortalDetector::Results& results) {
bool is_https = results.landing_url.SchemeIs("https");
switch (results.result) {
case RESULT_INTERNET_CONNECTED:
return DETECTION_RESULT_INTERNET_CONNECTED;
case RESULT_NO_RESPONSE:
return is_https ?
DETECTION_RESULT_NO_RESPONSE_HTTPS_LANDING_URL :
DETECTION_RESULT_NO_RESPONSE;
case RESULT_BEHIND_CAPTIVE_PORTAL:
return is_https ?
DETECTION_RESULT_BEHIND_CAPTIVE_PORTAL_HTTPS_LANDING_URL :
DETECTION_RESULT_BEHIND_CAPTIVE_PORTAL;
default:
NOTREACHED();
return -1;
}
}
bool ShouldDeferToNativeCaptivePortalDetection() {
#if defined(OS_WIN)
return base::win::GetVersion() >= base::win::VERSION_WIN8;
#else
return false;
#endif
}
}
CaptivePortalService::TestingState CaptivePortalService::testing_state_ =
NOT_TESTING;
class CaptivePortalService::RecheckBackoffEntry : public net::BackoffEntry {
public:
explicit RecheckBackoffEntry(CaptivePortalService* captive_portal_service)
: net::BackoffEntry(
&captive_portal_service->recheck_policy().backoff_policy),
captive_portal_service_(captive_portal_service) {
}
private:
virtual base::TimeTicks ImplGetTimeNow() const OVERRIDE {
return captive_portal_service_->GetCurrentTimeTicks();
}
CaptivePortalService* captive_portal_service_;
DISALLOW_COPY_AND_ASSIGN(RecheckBackoffEntry);
};
CaptivePortalService::RecheckPolicy::RecheckPolicy()
: initial_backoff_no_portal_ms(600 * 1000),
initial_backoff_portal_ms(20 * 1000) {
backoff_policy.num_errors_to_ignore = 6;
backoff_policy.initial_delay_ms = initial_backoff_no_portal_ms;
backoff_policy.multiply_factor = 2.0;
backoff_policy.jitter_factor = 0.3;
backoff_policy.maximum_backoff_ms = 2 * 60 * 1000;
backoff_policy.entry_lifetime_ms = -1;
backoff_policy.always_use_initial_delay = true;
}
CaptivePortalService::CaptivePortalService(Profile* profile)
: profile_(profile),
state_(STATE_IDLE),
captive_portal_detector_(profile->GetRequestContext()),
enabled_(false),
last_detection_result_(RESULT_INTERNET_CONNECTED),
num_checks_with_same_result_(0),
test_url_(CaptivePortalDetector::kDefaultURL) {
resolve_errors_with_web_service_.Init(
prefs::kAlternateErrorPagesEnabled,
profile_->GetPrefs(),
base::Bind(&CaptivePortalService::UpdateEnabledState,
base::Unretained(this)));
ResetBackoffEntry(last_detection_result_);
UpdateEnabledState();
}
CaptivePortalService::~CaptivePortalService() {
}
void CaptivePortalService::DetectCaptivePortal() {
DCHECK(CalledOnValidThread());
if (state_ == STATE_CHECKING_FOR_PORTAL || state_ == STATE_TIMER_RUNNING)
return;
base::TimeDelta time_until_next_check = backoff_entry_->GetTimeUntilRelease();
state_ = STATE_TIMER_RUNNING;
check_captive_portal_timer_.Start(
FROM_HERE,
time_until_next_check,
this,
&CaptivePortalService::DetectCaptivePortalInternal);
}
void CaptivePortalService::DetectCaptivePortalInternal() {
DCHECK(CalledOnValidThread());
DCHECK(state_ == STATE_TIMER_RUNNING || state_ == STATE_IDLE);
DCHECK(!TimerRunning());
state_ = STATE_CHECKING_FOR_PORTAL;
if (!enabled_) {
backoff_entry_->InformOfRequest(true);
OnResult(RESULT_INTERNET_CONNECTED);
return;
}
captive_portal_detector_.DetectCaptivePortal(
test_url_, base::Bind(
&CaptivePortalService::OnPortalDetectionCompleted,
base::Unretained(this)));
}
void CaptivePortalService::OnPortalDetectionCompleted(
const CaptivePortalDetector::Results& results) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(STATE_CHECKING_FOR_PORTAL, state_);
DCHECK(!TimerRunning());
DCHECK(enabled_);
Result result = results.result;
const base::TimeDelta& retry_after_delta = results.retry_after_delta;
base::TimeTicks now = GetCurrentTimeTicks();
UMA_HISTOGRAM_ENUMERATION("CaptivePortal.DetectResult",
GetHistogramEntryForDetectionResult(results),
DETECTION_RESULT_COUNT);
if (!last_check_time_.is_null()) {
UMA_HISTOGRAM_LONG_TIMES("CaptivePortal.TimeBetweenChecks",
now - last_check_time_);
if (last_detection_result_ != result) {
RecordRepeatHistograms(last_detection_result_,
num_checks_with_same_result_,
now - first_check_time_with_same_result_);
}
}
if (last_check_time_.is_null() || result != last_detection_result_) {
first_check_time_with_same_result_ = now;
num_checks_with_same_result_ = 1;
ResetBackoffEntry(result);
backoff_entry_->SetCustomReleaseTime(now + retry_after_delta);
} else {
DCHECK_LE(1, num_checks_with_same_result_);
++num_checks_with_same_result_;
backoff_entry_->SetCustomReleaseTime(now + retry_after_delta);
backoff_entry_->InformOfRequest(false);
}
last_check_time_ = now;
OnResult(result);
}
void CaptivePortalService::Shutdown() {
DCHECK(CalledOnValidThread());
if (enabled_) {
RecordRepeatHistograms(
last_detection_result_,
num_checks_with_same_result_,
GetCurrentTimeTicks() - first_check_time_with_same_result_);
}
}
void CaptivePortalService::OnResult(Result result) {
DCHECK_EQ(STATE_CHECKING_FOR_PORTAL, state_);
state_ = STATE_IDLE;
Results results;
results.previous_result = last_detection_result_;
results.result = result;
last_detection_result_ = result;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
content::Source<Profile>(profile_),
content::Details<Results>(&results));
}
void CaptivePortalService::ResetBackoffEntry(Result result) {
if (!enabled_ || result == RESULT_BEHIND_CAPTIVE_PORTAL) {
recheck_policy_.backoff_policy.initial_delay_ms =
recheck_policy_.initial_backoff_portal_ms;
} else {
recheck_policy_.backoff_policy.initial_delay_ms =
recheck_policy_.initial_backoff_no_portal_ms;
}
backoff_entry_.reset(new RecheckBackoffEntry(this));
}
void CaptivePortalService::UpdateEnabledState() {
DCHECK(CalledOnValidThread());
bool enabled_before = enabled_;
enabled_ = testing_state_ != DISABLED_FOR_TESTING &&
resolve_errors_with_web_service_.GetValue();
if (testing_state_ != SKIP_OS_CHECK_FOR_TESTING &&
ShouldDeferToNativeCaptivePortalDetection()) {
enabled_ = false;
}
if (enabled_before == enabled_)
return;
num_checks_with_same_result_ = 0;
first_check_time_with_same_result_ = base::TimeTicks();
last_check_time_ = base::TimeTicks();
ResetBackoffEntry(last_detection_result_);
if (state_ == STATE_CHECKING_FOR_PORTAL || state_ == STATE_TIMER_RUNNING) {
check_captive_portal_timer_.Stop();
captive_portal_detector_.Cancel();
state_ = STATE_IDLE;
DetectCaptivePortal();
}
}
base::TimeTicks CaptivePortalService::GetCurrentTimeTicks() const {
if (time_ticks_for_testing_.is_null())
return base::TimeTicks::Now();
else
return time_ticks_for_testing_;
}
bool CaptivePortalService::DetectionInProgress() const {
return state_ == STATE_CHECKING_FOR_PORTAL;
}
bool CaptivePortalService::TimerRunning() const {
return check_captive_portal_timer_.IsRunning();
}
}