blob: 625f3e1f3c71fced700b34584610fd941a86dee0 [file] [log] [blame]
Paul Stewarte6927402012-01-23 16:11:30 -08001// Copyright (c) 2012 The Chromium OS 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#include "shill/portal_detector.h"
6
7#include <string>
8
Eric Shienbrood3e20a232012-02-16 11:35:56 -05009#include <base/bind.h>
Ben Chana0ddf462014-02-06 11:32:42 -080010#include <base/strings/string_number_conversions.h>
11#include <base/strings/string_util.h>
12#include <base/strings/stringprintf.h>
Peter Qiu9b83c892014-08-09 23:06:02 -070013#include <chromeos/dbus/service_constants.h>
Paul Stewarte6927402012-01-23 16:11:30 -080014
Paul Stewarte6927402012-01-23 16:11:30 -080015#include "shill/connection.h"
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070016#include "shill/connectivity_trial.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070017#include "shill/logging.h"
Paul Stewarte6927402012-01-23 16:11:30 -080018
Eric Shienbrood3e20a232012-02-16 11:35:56 -050019using base::Bind;
20using base::Callback;
Paul Stewarte6927402012-01-23 16:11:30 -080021using base::StringPrintf;
22using std::string;
23
24namespace shill {
25
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070026namespace Logging {
27static auto kModuleLogScope = ScopeLogger::kPortal;
Paul Stewart1a212a62015-06-16 13:13:10 -070028static string ObjectID(Connection* c) { return c->interface_name(); }
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070029}
30
Paul Stewartc681fa02012-03-02 19:40:04 -080031const int PortalDetector::kDefaultCheckIntervalSeconds = 30;
Paul Stewartf555cf82012-03-15 14:42:43 -070032const char PortalDetector::kDefaultCheckPortalList[] = "ethernet,wifi,cellular";
Paul Stewarte6927402012-01-23 16:11:30 -080033
34const int PortalDetector::kMaxRequestAttempts = 3;
35const int PortalDetector::kMinTimeBetweenAttemptsSeconds = 3;
36const int PortalDetector::kRequestTimeoutSeconds = 10;
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -070037const int PortalDetector::kMaxFailuresInContentPhase = 2;
Paul Stewarte6927402012-01-23 16:11:30 -080038
Paul Stewarte6927402012-01-23 16:11:30 -080039PortalDetector::PortalDetector(
40 ConnectionRefPtr connection,
Paul Stewart1a212a62015-06-16 13:13:10 -070041 EventDispatcher* dispatcher,
42 const Callback<void(const PortalDetector::Result&)>& callback)
Paul Stewarte6927402012-01-23 16:11:30 -080043 : attempt_count_(0),
Darin Petkove636c692012-05-31 10:22:17 +020044 attempt_start_time_((struct timeval){0}),
Paul Stewarte6927402012-01-23 16:11:30 -080045 connection_(connection),
46 dispatcher_(dispatcher),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050047 weak_ptr_factory_(this),
Paul Stewarte6927402012-01-23 16:11:30 -080048 portal_result_callback_(callback),
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070049 connectivity_trial_callback_(Bind(&PortalDetector::CompleteAttempt,
50 weak_ptr_factory_.GetWeakPtr())),
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -070051 time_(Time::GetInstance()),
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070052 failures_in_content_phase_(0),
53 connectivity_trial_(
54 new ConnectivityTrial(connection_,
55 dispatcher_,
56 kRequestTimeoutSeconds,
57 connectivity_trial_callback_)) { }
Paul Stewarte6927402012-01-23 16:11:30 -080058
59PortalDetector::~PortalDetector() {
60 Stop();
61}
62
Paul Stewart1a212a62015-06-16 13:13:10 -070063bool PortalDetector::Start(const string& url_string) {
Paul Stewartc681fa02012-03-02 19:40:04 -080064 return StartAfterDelay(url_string, 0);
65}
66
Paul Stewart1a212a62015-06-16 13:13:10 -070067bool PortalDetector::StartAfterDelay(const string& url_string,
Paul Stewartc681fa02012-03-02 19:40:04 -080068 int delay_seconds) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -080069 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewarte6927402012-01-23 16:11:30 -080070
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070071 if (!connectivity_trial_->Start(url_string, delay_seconds * 1000)) {
Paul Stewarte6927402012-01-23 16:11:30 -080072 return false;
73 }
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070074 attempt_count_ = 1;
75 // The attempt_start_time_ is calculated based on the current time and
76 // |delay_seconds|. This is used to determine if a portal detection attempt
77 // is in progress.
78 UpdateAttemptTime(delay_seconds);
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -070079 // If we're starting a new set of attempts, discard past failure history.
80 failures_in_content_phase_ = 0;
Paul Stewarte6927402012-01-23 16:11:30 -080081 return true;
82}
83
84void PortalDetector::Stop() {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -080085 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewarte6927402012-01-23 16:11:30 -080086
Paul Stewarte6927402012-01-23 16:11:30 -080087 attempt_count_ = 0;
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -070088 failures_in_content_phase_ = 0;
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070089 if (connectivity_trial_.get())
90 connectivity_trial_->Stop();
Paul Stewarte6927402012-01-23 16:11:30 -080091}
92
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -070093// IsInProgress returns true if a ConnectivityTrial is actively testing the
94// connection. If Start has been called, but the trial was delayed,
95// IsInProgress will return false. PortalDetector implements this by
96// calculating the start time of the next ConnectivityTrial. After an initial
97// trial and in the case where multiple attempts may be tried, IsInProgress will
98// return true.
Paul Stewartc681fa02012-03-02 19:40:04 -080099bool PortalDetector::IsInProgress() {
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700100 if (attempt_count_ > 1)
101 return true;
102 if (attempt_count_ == 1 && connectivity_trial_.get())
103 return connectivity_trial_->IsActive();
104 return false;
Paul Stewartc681fa02012-03-02 19:40:04 -0800105}
106
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700107void PortalDetector::CompleteAttempt(ConnectivityTrial::Result trial_result) {
108 Result result = Result(trial_result);
109 if (trial_result.status == ConnectivityTrial::kStatusFailure &&
110 trial_result.phase == ConnectivityTrial::kPhaseContent) {
111 failures_in_content_phase_++;
Paul Stewarte6927402012-01-23 16:11:30 -0800112 }
Paul Stewarte6927402012-01-23 16:11:30 -0800113
Paul Stewarte6927402012-01-23 16:11:30 -0800114 LOG(INFO) << StringPrintf("Portal detection completed attempt %d with "
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700115 "phase==%s, status==%s, failures in content==%d",
Paul Stewarte6927402012-01-23 16:11:30 -0800116 attempt_count_,
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700117 ConnectivityTrial::PhaseToString(
118 trial_result.phase).c_str(),
119 ConnectivityTrial::StatusToString(
120 trial_result.status).c_str(),
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700121 failures_in_content_phase_);
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700122
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700123 if (trial_result.status == ConnectivityTrial::kStatusSuccess ||
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700124 attempt_count_ >= kMaxRequestAttempts ||
125 failures_in_content_phase_ >= kMaxFailuresInContentPhase) {
Thieu Le85e050b2012-03-13 15:04:38 -0700126 result.num_attempts = attempt_count_;
Paul Stewarte6927402012-01-23 16:11:30 -0800127 result.final = true;
128 Stop();
Christopher Wiley6e1dc0f2012-10-17 15:38:56 -0700129 } else {
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700130 attempt_count_++;
131 int retry_delay_seconds = AdjustStartDelay(0);
132 connectivity_trial_->Retry(retry_delay_seconds * 1000);
133 UpdateAttemptTime(retry_delay_seconds);
Paul Stewarte6927402012-01-23 16:11:30 -0800134 }
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500135 portal_result_callback_.Run(result);
Paul Stewarte6927402012-01-23 16:11:30 -0800136}
137
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700138void PortalDetector::UpdateAttemptTime(int delay_seconds) {
139 time_->GetTimeMonotonic(&attempt_start_time_);
140 struct timeval delay_timeval = { delay_seconds, 0 };
141 timeradd(&attempt_start_time_, &delay_timeval, &attempt_start_time_);
Paul Stewarte6927402012-01-23 16:11:30 -0800142}
143
Paul Stewarte6927402012-01-23 16:11:30 -0800144
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700145int PortalDetector::AdjustStartDelay(int init_delay_seconds) {
146 int next_attempt_delay_seconds = 0;
Paul Stewarte6927402012-01-23 16:11:30 -0800147 if (attempt_count_ > 0) {
148 // Ensure that attempts are spaced at least by a minimal interval.
149 struct timeval now, elapsed_time;
150 time_->GetTimeMonotonic(&now);
151 timersub(&now, &attempt_start_time_, &elapsed_time);
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800152 SLOG(connection_.get(), 4) << "Elapsed time from previous attempt is "
153 << elapsed_time.tv_sec << " seconds.";
Paul Stewarte6927402012-01-23 16:11:30 -0800154 if (elapsed_time.tv_sec < kMinTimeBetweenAttemptsSeconds) {
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700155 next_attempt_delay_seconds = kMinTimeBetweenAttemptsSeconds -
156 elapsed_time.tv_sec;
Paul Stewarte6927402012-01-23 16:11:30 -0800157 }
Paul Stewartc681fa02012-03-02 19:40:04 -0800158 } else {
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700159 LOG(FATAL) << "AdjustStartDelay in PortalDetector called without "
160 "previous attempts";
Paul Stewarte6927402012-01-23 16:11:30 -0800161 }
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800162 SLOG(connection_.get(), 3) << "Adjusting trial start delay from "
163 << init_delay_seconds << " seconds to "
164 << next_attempt_delay_seconds << " seconds.";
Rebecca Silberstein3d49ea42014-08-21 11:20:50 -0700165 return next_attempt_delay_seconds;
Paul Stewarte6927402012-01-23 16:11:30 -0800166}
167
168} // namespace shill