summaryrefslogtreecommitdiffstats
path: root/chrome/browser/captive_portal/captive_portal_service.h
blob: c8b597049ed70dffe10c9ce6d455e38b3bfb1af9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// 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/pref_member.h"
#include "base/threading/non_thread_safe.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/captive_portal/captive_portal_detector.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
#include "net/base/backoff_entry.h"
#include "url/gurl.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 BrowserContextKeyedService,
                             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);

  // BrowserContextKeyedService:
  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_