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();
}
}