1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
6#define CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
7
8#include "base/basictypes.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/prefs/pref_member.h"
11#include "base/threading/non_thread_safe.h"
12#include "base/time/time.h"
13#include "base/timer/timer.h"
14#include "chrome/browser/captive_portal/captive_portal_detector.h"
15#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
16#include "net/base/backoff_entry.h"
17#include "url/gurl.h"
18
19class Profile;
20
21namespace captive_portal {
22
23// Service that checks for captive portals when queried, and sends a
24// NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT with the Profile as the source and
25// a CaptivePortalService::Results as the details.
26//
27// Captive portal checks are rate-limited.  The CaptivePortalService may only
28// be accessed on the UI thread.
29// Design doc: https://docs.google.com/document/d/1k-gP2sswzYNvryu9NcgN7q5XrsMlUdlUdoW9WRaEmfM/edit
30class CaptivePortalService : public BrowserContextKeyedService,
31                             public base::NonThreadSafe {
32 public:
33  enum TestingState {
34    NOT_TESTING,
35    DISABLED_FOR_TESTING,  // The service is always disabled.
36    SKIP_OS_CHECK_FOR_TESTING  // The service can be enabled even if the OS has
37                               // native captive portal detection.
38  };
39
40  // The details sent via a NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT.
41  struct Results {
42    // The result of the second most recent captive portal check.
43    Result previous_result;
44    // The result of the most recent captive portal check.
45    Result result;
46  };
47
48  explicit CaptivePortalService(Profile* profile);
49  virtual ~CaptivePortalService();
50
51  // Triggers a check for a captive portal.  If there's already a check in
52  // progress, does nothing.  Throttles the rate at which requests are sent.
53  // Always sends the result notification asynchronously.
54  void DetectCaptivePortal();
55
56  // Returns the URL used for captive portal testing.  When a captive portal is
57  // detected, this URL will take us to the captive portal landing page.
58  const GURL& test_url() const { return test_url_; }
59
60  // Result of the most recent captive portal check.
61  Result last_detection_result() const { return last_detection_result_; }
62
63  // Whether or not the CaptivePortalService is enabled.  When disabled, all
64  // checks return INTERNET_CONNECTED.
65  bool enabled() const { return enabled_; }
66
67  // Used to disable captive portal detection so it doesn't interfere with
68  // tests.  Should be called before the service is created.
69  static void set_state_for_testing(TestingState testing_state) {
70    testing_state_ = testing_state;
71  }
72  static TestingState get_state_for_testing() { return testing_state_; }
73
74 private:
75  friend class CaptivePortalServiceTest;
76  friend class CaptivePortalBrowserTest;
77
78  // Subclass of BackoffEntry that uses the CaptivePortalService's
79  // GetCurrentTime function, for unit testing.
80  class RecheckBackoffEntry;
81
82  enum State {
83    // No check is running or pending.
84    STATE_IDLE,
85    // The timer to check for a captive portal is running.
86    STATE_TIMER_RUNNING,
87    // There's an outstanding HTTP request to check for a captive portal.
88    STATE_CHECKING_FOR_PORTAL,
89  };
90
91  // Contains all the information about the minimum time allowed between two
92  // consecutive captive portal checks.
93  struct RecheckPolicy {
94    // Constructor initializes all values to defaults.
95    RecheckPolicy();
96
97    // The minimum amount of time between two captive portal checks, when the
98    // last check found no captive portal.
99    int initial_backoff_no_portal_ms;
100
101    // The minimum amount of time between two captive portal checks, when the
102    // last check found a captive portal.  This is expected to be less than
103    // |initial_backoff_no_portal_ms|.  Also used when the service is disabled.
104    int initial_backoff_portal_ms;
105
106    net::BackoffEntry::Policy backoff_policy;
107  };
108
109  // Initiates a captive portal check, without any throttling.  If the service
110  // is disabled, just acts like there's an Internet connection.
111  void DetectCaptivePortalInternal();
112
113  // Called by CaptivePortalDetector when detection completes.
114  void OnPortalDetectionCompleted(
115      const CaptivePortalDetector::Results& results);
116
117  // BrowserContextKeyedService:
118  virtual void Shutdown() OVERRIDE;
119
120  // Called when a captive portal check completes.  Passes the result to all
121  // observers.
122  void OnResult(Result result);
123
124  // Updates BackoffEntry::Policy and creates a new BackoffEntry, which
125  // resets the count used for throttling.
126  void ResetBackoffEntry(Result result);
127
128  // Updates |enabled_| based on command line flags and Profile preferences,
129  // and sets |state_| to STATE_NONE if it's false.
130  // TODO(mmenke): Figure out on which platforms, if any, should not use
131  //               automatic captive portal detection.  Currently it's enabled
132  //               on all platforms, though this code is not compiled on
133  //               Android, since it lacks the Browser class.
134  void UpdateEnabledState();
135
136  // Returns the current TimeTicks.
137  base::TimeTicks GetCurrentTimeTicks() const;
138
139  bool DetectionInProgress() const;
140
141  // Returns true if the timer to try and detect a captive portal is running.
142  bool TimerRunning() const;
143
144  State state() const { return state_; }
145
146  RecheckPolicy& recheck_policy() { return recheck_policy_; }
147
148  void set_test_url(const GURL& test_url) { test_url_ = test_url; }
149
150  // Sets current test time ticks. Used by unit tests.
151  void set_time_ticks_for_testing(const base::TimeTicks& time_ticks) {
152    time_ticks_for_testing_ = time_ticks;
153  }
154
155  // Advances current test time ticks. Used by unit tests.
156  void advance_time_ticks_for_testing(const base::TimeDelta& delta) {
157    time_ticks_for_testing_ += delta;
158  }
159
160  // The profile that owns this CaptivePortalService.
161  Profile* profile_;
162
163  State state_;
164
165  // Detector for checking active network for a portal state.
166  CaptivePortalDetector captive_portal_detector_;
167
168  // True if the service is enabled.  When not enabled, all checks will return
169  // RESULT_INTERNET_CONNECTED.
170  bool enabled_;
171
172  // The result of the most recent captive portal check.
173  Result last_detection_result_;
174
175  // Number of sequential checks with the same captive portal result.
176  int num_checks_with_same_result_;
177
178  // Time when |last_detection_result_| was first received.
179  base::TimeTicks first_check_time_with_same_result_;
180
181  // Time the last captive portal check completed.
182  base::TimeTicks last_check_time_;
183
184  // Policy for throttling portal checks.
185  RecheckPolicy recheck_policy_;
186
187  // Implements behavior needed by |recheck_policy_|.  Whenever there's a new
188  // captive_portal::Result, BackoffEntry::Policy is updated and
189  // |backoff_entry_| is recreated.  Each check that returns the same Result
190  // is considered a "failure", to trigger throttling.
191  scoped_ptr<net::BackoffEntry> backoff_entry_;
192
193  // URL that returns a 204 response code when connected to the Internet.
194  GURL test_url_;
195
196  // The pref member for whether navigation errors should be resolved with a web
197  // service.  Actually called "alternate_error_pages", since it's also used for
198  // the Link Doctor.
199  BooleanPrefMember resolve_errors_with_web_service_;
200
201  base::OneShotTimer<CaptivePortalService> check_captive_portal_timer_;
202
203  static TestingState testing_state_;
204
205  // Test time ticks used by unit tests.
206  base::TimeTicks time_ticks_for_testing_;
207
208  DISALLOW_COPY_AND_ASSIGN(CaptivePortalService);
209};
210
211}  // namespace captive_portal
212
213#endif  // CHROME_BROWSER_CAPTIVE_PORTAL_CAPTIVE_PORTAL_SERVICE_H_
214