text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
  #define CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
  
  #include "base/basictypes.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/prefs/public/pref_observer.h"
  #include "base/threading/non_thread_safe.h"
  #include "base/time.h"
  #include "base/timer.h"
  #include "chrome/browser/api/prefs/pref_member.h"
  #include "chrome/browser/captive_portal/captive_portal_detector.h"
  #include "chrome/browser/profiles/profile_keyed_service.h"
  #include "googleurl/src/gurl.h"
  #include "net/base/backoff_entry.h"
  
  class Profile;
  
  namespace captive_portal {
  
  // Service that checks for captive portals when queried, and sends a
  // NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT with the Profile as the source and
  // a CaptivePortalService::Results as the details.
  //
  // Captive portal checks are rate-limited.  The CaptivePortalService may only
  // be accessed on the UI thread.
  // Design doc: https://docs.google.com/document/d/1k-gP2sswzYNvryu9NcgN7q5XrsMlUdlUdoW9WRaEmfM/edit
  class CaptivePortalService : public ProfileKeyedService,
                               public PrefObserver,
                               public base::NonThreadSafe {
   public:
    enum TestingState {
      NOT_TESTING,
      DISABLED_FOR_TESTING,  // The service is always disabled.
      SKIP_OS_CHECK_FOR_TESTING  // The service can be enabled even if the OS has
                                 // native captive portal detection.
    };
  
    // The details sent via a NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT.
    struct Results {
      // The result of the second most recent captive portal check.
      Result previous_result;
      // The result of the most recent captive portal check.
      Result result;
    };
  
    explicit CaptivePortalService(Profile* profile);
    virtual ~CaptivePortalService();
  
    // Triggers a check for a captive portal.  If there's already a check in
    // progress, does nothing.  Throttles the rate at which requests are sent.
    // Always sends the result notification asynchronously.
    void DetectCaptivePortal();
  
    // Returns the URL used for captive portal testing.  When a captive portal is
    // detected, this URL will take us to the captive portal landing page.
    const GURL& test_url() const { return test_url_; }
  
    // Result of the most recent captive portal check.
    Result last_detection_result() const { return last_detection_result_; }
  
    // Whether or not the CaptivePortalService is enabled.  When disabled, all
    // checks return INTERNET_CONNECTED.
    bool enabled() const { return enabled_; }
  
    // Used to disable captive portal detection so it doesn't interfere with
    // tests.  Should be called before the service is created.
    static void set_state_for_testing(TestingState testing_state) {
      testing_state_ = testing_state;
    }
    static TestingState get_state_for_testing() { return testing_state_; }
  
   private:
    friend class CaptivePortalServiceTest;
    friend class CaptivePortalBrowserTest;
  
    // Subclass of BackoffEntry that uses the CaptivePortalService's
    // GetCurrentTime function, for unit testing.
    class RecheckBackoffEntry;
  
    enum State {
      // No check is running or pending.
      STATE_IDLE,
      // The timer to check for a captive portal is running.
      STATE_TIMER_RUNNING,
      // There's an outstanding HTTP request to check for a captive portal.
      STATE_CHECKING_FOR_PORTAL,
    };
  
    // Contains all the information about the minimum time allowed between two
    // consecutive captive portal checks.
    struct RecheckPolicy {
      // Constructor initializes all values to defaults.
      RecheckPolicy();
  
      // The minimum amount of time between two captive portal checks, when the
      // last check found no captive portal.
      int initial_backoff_no_portal_ms;
  
      // The minimum amount of time between two captive portal checks, when the
      // last check found a captive portal.  This is expected to be less than
      // |initial_backoff_no_portal_ms|.  Also used when the service is disabled.
      int initial_backoff_portal_ms;
  
      net::BackoffEntry::Policy backoff_policy;
    };
  
    // Initiates a captive portal check, without any throttling.  If the service
    // is disabled, just acts like there's an Internet connection.
    void DetectCaptivePortalInternal();
  
    // Called by CaptivePortalDetector when detection completes.
    void OnPortalDetectionCompleted(
        const CaptivePortalDetector::Results& results);
  
    // PrefObserver
    virtual void OnPreferenceChanged(PrefServiceBase* service,
                                     const std::string& pref_name) OVERRIDE;
  
    // ProfileKeyedService:
    virtual void Shutdown() OVERRIDE;
  
    // Called when a captive portal check completes.  Passes the result to all
    // observers.
    void OnResult(Result result);
  
    // Updates BackoffEntry::Policy and creates a new BackoffEntry, which
    // resets the count used for throttling.
    void ResetBackoffEntry(Result result);
  
    // Updates |enabled_| based on command line flags and Profile preferences,
    // and sets |state_| to STATE_NONE if it's false.
    // TODO(mmenke): Figure out on which platforms, if any, should not use
    //               automatic captive portal detection.  Currently it's enabled
    //               on all platforms, though this code is not compiled on
    //               Android, since it lacks the Browser class.
    void UpdateEnabledState();
  
    // Returns the current TimeTicks.
    base::TimeTicks GetCurrentTimeTicks() const;
  
    bool DetectionInProgress() const;
  
    // Returns true if the timer to try and detect a captive portal is running.
    bool TimerRunning() const;
  
    State state() const { return state_; }
  
    RecheckPolicy& recheck_policy() { return recheck_policy_; }
  
    void set_test_url(const GURL& test_url) { test_url_ = test_url; }
  
    // Sets current test time ticks. Used by unit tests.
    void set_time_ticks_for_testing(const base::TimeTicks& time_ticks) {
      time_ticks_for_testing_ = time_ticks;
    }
  
    // Advances current test time ticks. Used by unit tests.
    void advance_time_ticks_for_testing(const base::TimeDelta& delta) {
      time_ticks_for_testing_ += delta;
    }
  
    // The profile that owns this CaptivePortalService.
    Profile* profile_;
  
    State state_;
  
    // Detector for checking active network for a portal state.
    CaptivePortalDetector captive_portal_detector_;
  
    // True if the service is enabled.  When not enabled, all checks will return
    // RESULT_INTERNET_CONNECTED.
    bool enabled_;
  
    // The result of the most recent captive portal check.
    Result last_detection_result_;
  
    // Number of sequential checks with the same captive portal result.
    int num_checks_with_same_result_;
  
    // Time when |last_detection_result_| was first received.
    base::TimeTicks first_check_time_with_same_result_;
  
    // Time the last captive portal check completed.
    base::TimeTicks last_check_time_;
  
    // Policy for throttling portal checks.
    RecheckPolicy recheck_policy_;
  
    // Implements behavior needed by |recheck_policy_|.  Whenever there's a new
    // captive_portal::Result, BackoffEntry::Policy is updated and
    // |backoff_entry_| is recreated.  Each check that returns the same Result
    // is considered a "failure", to trigger throttling.
    scoped_ptr<net::BackoffEntry> backoff_entry_;
  
    // URL that returns a 204 response code when connected to the Internet.
    GURL test_url_;
  
    // The pref member for whether navigation errors should be resolved with a web
    // service.  Actually called "alternate_error_pages", since it's also used for
    // the Link Doctor.
    BooleanPrefMember resolve_errors_with_web_service_;
  
    base::OneShotTimer<CaptivePortalService> check_captive_portal_timer_;
  
    static TestingState testing_state_;
  
    // Test time ticks used by unit tests.
    base::TimeTicks time_ticks_for_testing_;
  
    DISALLOW_COPY_AND_ASSIGN(CaptivePortalService);
  };
  
  }  // namespace captive_portal
  
  #endif  // CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
  