Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 1 | // 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 Shienbrood | 3e20a23 | 2012-02-16 11:35:56 -0500 | [diff] [blame] | 9 | #include <base/bind.h> |
Ben Chan | a0ddf46 | 2014-02-06 11:32:42 -0800 | [diff] [blame] | 10 | #include <base/strings/string_number_conversions.h> |
| 11 | #include <base/strings/string_util.h> |
| 12 | #include <base/strings/stringprintf.h> |
Peter Qiu | 9b83c89 | 2014-08-09 23:06:02 -0700 | [diff] [blame] | 13 | #include <chromeos/dbus/service_constants.h> |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 14 | |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 15 | #include "shill/connection.h" |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 16 | #include "shill/connectivity_trial.h" |
Christopher Wiley | b691efd | 2012-08-09 13:51:51 -0700 | [diff] [blame] | 17 | #include "shill/logging.h" |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 18 | |
Eric Shienbrood | 3e20a23 | 2012-02-16 11:35:56 -0500 | [diff] [blame] | 19 | using base::Bind; |
| 20 | using base::Callback; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 21 | using base::StringPrintf; |
| 22 | using std::string; |
| 23 | |
| 24 | namespace shill { |
| 25 | |
Rebecca Silberstein | c9c31d8 | 2014-10-21 15:01:00 -0700 | [diff] [blame] | 26 | namespace Logging { |
| 27 | static auto kModuleLogScope = ScopeLogger::kPortal; |
Paul Stewart | 1a212a6 | 2015-06-16 13:13:10 -0700 | [diff] [blame] | 28 | static string ObjectID(Connection* c) { return c->interface_name(); } |
Rebecca Silberstein | c9c31d8 | 2014-10-21 15:01:00 -0700 | [diff] [blame] | 29 | } |
| 30 | |
Paul Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 31 | const int PortalDetector::kDefaultCheckIntervalSeconds = 30; |
Paul Stewart | f555cf8 | 2012-03-15 14:42:43 -0700 | [diff] [blame] | 32 | const char PortalDetector::kDefaultCheckPortalList[] = "ethernet,wifi,cellular"; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 33 | |
| 34 | const int PortalDetector::kMaxRequestAttempts = 3; |
| 35 | const int PortalDetector::kMinTimeBetweenAttemptsSeconds = 3; |
| 36 | const int PortalDetector::kRequestTimeoutSeconds = 10; |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 37 | const int PortalDetector::kMaxFailuresInContentPhase = 2; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 38 | |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 39 | PortalDetector::PortalDetector( |
| 40 | ConnectionRefPtr connection, |
Paul Stewart | 1a212a6 | 2015-06-16 13:13:10 -0700 | [diff] [blame] | 41 | EventDispatcher* dispatcher, |
| 42 | const Callback<void(const PortalDetector::Result&)>& callback) |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 43 | : attempt_count_(0), |
Darin Petkov | e636c69 | 2012-05-31 10:22:17 +0200 | [diff] [blame] | 44 | attempt_start_time_((struct timeval){0}), |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 45 | connection_(connection), |
| 46 | dispatcher_(dispatcher), |
Eric Shienbrood | 3e20a23 | 2012-02-16 11:35:56 -0500 | [diff] [blame] | 47 | weak_ptr_factory_(this), |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 48 | portal_result_callback_(callback), |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 49 | connectivity_trial_callback_(Bind(&PortalDetector::CompleteAttempt, |
| 50 | weak_ptr_factory_.GetWeakPtr())), |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 51 | time_(Time::GetInstance()), |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 52 | failures_in_content_phase_(0), |
| 53 | connectivity_trial_( |
| 54 | new ConnectivityTrial(connection_, |
| 55 | dispatcher_, |
| 56 | kRequestTimeoutSeconds, |
| 57 | connectivity_trial_callback_)) { } |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 58 | |
| 59 | PortalDetector::~PortalDetector() { |
| 60 | Stop(); |
| 61 | } |
| 62 | |
Paul Stewart | 1a212a6 | 2015-06-16 13:13:10 -0700 | [diff] [blame] | 63 | bool PortalDetector::Start(const string& url_string) { |
Paul Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 64 | return StartAfterDelay(url_string, 0); |
| 65 | } |
| 66 | |
Paul Stewart | 1a212a6 | 2015-06-16 13:13:10 -0700 | [diff] [blame] | 67 | bool PortalDetector::StartAfterDelay(const string& url_string, |
Paul Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 68 | int delay_seconds) { |
Alex Vakulenko | 0951ccb | 2014-12-10 12:52:31 -0800 | [diff] [blame] | 69 | SLOG(connection_.get(), 3) << "In " << __func__; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 70 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 71 | if (!connectivity_trial_->Start(url_string, delay_seconds * 1000)) { |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 72 | return false; |
| 73 | } |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 74 | 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 Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 79 | // If we're starting a new set of attempts, discard past failure history. |
| 80 | failures_in_content_phase_ = 0; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 81 | return true; |
| 82 | } |
| 83 | |
| 84 | void PortalDetector::Stop() { |
Alex Vakulenko | 0951ccb | 2014-12-10 12:52:31 -0800 | [diff] [blame] | 85 | SLOG(connection_.get(), 3) << "In " << __func__; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 86 | |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 87 | attempt_count_ = 0; |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 88 | failures_in_content_phase_ = 0; |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 89 | if (connectivity_trial_.get()) |
| 90 | connectivity_trial_->Stop(); |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 91 | } |
| 92 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 93 | // 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 Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 99 | bool PortalDetector::IsInProgress() { |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 100 | if (attempt_count_ > 1) |
| 101 | return true; |
| 102 | if (attempt_count_ == 1 && connectivity_trial_.get()) |
| 103 | return connectivity_trial_->IsActive(); |
| 104 | return false; |
Paul Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 105 | } |
| 106 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 107 | void 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 Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 112 | } |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 113 | |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 114 | LOG(INFO) << StringPrintf("Portal detection completed attempt %d with " |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 115 | "phase==%s, status==%s, failures in content==%d", |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 116 | attempt_count_, |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 117 | ConnectivityTrial::PhaseToString( |
| 118 | trial_result.phase).c_str(), |
| 119 | ConnectivityTrial::StatusToString( |
| 120 | trial_result.status).c_str(), |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 121 | failures_in_content_phase_); |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 122 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 123 | if (trial_result.status == ConnectivityTrial::kStatusSuccess || |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 124 | attempt_count_ >= kMaxRequestAttempts || |
| 125 | failures_in_content_phase_ >= kMaxFailuresInContentPhase) { |
Thieu Le | 85e050b | 2012-03-13 15:04:38 -0700 | [diff] [blame] | 126 | result.num_attempts = attempt_count_; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 127 | result.final = true; |
| 128 | Stop(); |
Christopher Wiley | 6e1dc0f | 2012-10-17 15:38:56 -0700 | [diff] [blame] | 129 | } else { |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 130 | attempt_count_++; |
| 131 | int retry_delay_seconds = AdjustStartDelay(0); |
| 132 | connectivity_trial_->Retry(retry_delay_seconds * 1000); |
| 133 | UpdateAttemptTime(retry_delay_seconds); |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 134 | } |
Eric Shienbrood | 3e20a23 | 2012-02-16 11:35:56 -0500 | [diff] [blame] | 135 | portal_result_callback_.Run(result); |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 136 | } |
| 137 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 138 | void 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 Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 142 | } |
| 143 | |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 144 | |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 145 | int PortalDetector::AdjustStartDelay(int init_delay_seconds) { |
| 146 | int next_attempt_delay_seconds = 0; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 147 | 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 Vakulenko | 0951ccb | 2014-12-10 12:52:31 -0800 | [diff] [blame] | 152 | SLOG(connection_.get(), 4) << "Elapsed time from previous attempt is " |
| 153 | << elapsed_time.tv_sec << " seconds."; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 154 | if (elapsed_time.tv_sec < kMinTimeBetweenAttemptsSeconds) { |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 155 | next_attempt_delay_seconds = kMinTimeBetweenAttemptsSeconds - |
| 156 | elapsed_time.tv_sec; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 157 | } |
Paul Stewart | c681fa0 | 2012-03-02 19:40:04 -0800 | [diff] [blame] | 158 | } else { |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 159 | LOG(FATAL) << "AdjustStartDelay in PortalDetector called without " |
| 160 | "previous attempts"; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 161 | } |
Alex Vakulenko | 0951ccb | 2014-12-10 12:52:31 -0800 | [diff] [blame] | 162 | SLOG(connection_.get(), 3) << "Adjusting trial start delay from " |
| 163 | << init_delay_seconds << " seconds to " |
| 164 | << next_attempt_delay_seconds << " seconds."; |
Rebecca Silberstein | 3d49ea4 | 2014-08-21 11:20:50 -0700 | [diff] [blame] | 165 | return next_attempt_delay_seconds; |
Paul Stewart | e692740 | 2012-01-23 16:11:30 -0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | } // namespace shill |