blob: f1db22362d4a260acad81ece38511c8efe99c701 [file] [log] [blame]
Wade Guthriea60a11c2013-04-12 17:47:34 -07001// Copyright (c) 2013 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/scan_session.h"
6
7#include <algorithm>
8#include <set>
Wade Guthrieb9c3feb2013-04-25 16:31:19 -07009#include <string>
Wade Guthriea60a11c2013-04-12 17:47:34 -070010#include <vector>
11
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070012#include <base/bind.h>
13#include <base/memory/weak_ptr.h>
Wade Guthriea60a11c2013-04-12 17:47:34 -070014#include <base/stl_util.h>
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070015#include <base/stringprintf.h>
Wade Guthriea60a11c2013-04-12 17:47:34 -070016
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070017#include "shill/event_dispatcher.h"
Wade Guthriea60a11c2013-04-12 17:47:34 -070018#include "shill/logging.h"
Wade Guthrief22681f2013-05-31 11:46:31 -070019#include "shill/metrics.h"
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070020#include "shill/netlink_manager.h"
21#include "shill/nl80211_attribute.h"
22#include "shill/nl80211_message.h"
Wade Guthriea60a11c2013-04-12 17:47:34 -070023
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070024using base::Bind;
25using base::StringPrintf;
Wade Guthriea60a11c2013-04-12 17:47:34 -070026using std::set;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070027using std::string;
Wade Guthriea60a11c2013-04-12 17:47:34 -070028using std::vector;
29
30namespace shill {
31
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070032const float ScanSession::kAllFrequencies = 1.1;
Wade Guthrie086eb1e2013-05-31 17:31:13 -070033const uint64_t ScanSession::kScanRetryDelayMilliseconds = 200; // Arbitrary.
34const size_t ScanSession::kScanRetryCount = 50;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070035
Wade Guthriea60a11c2013-04-12 17:47:34 -070036ScanSession::ScanSession(
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070037 NetlinkManager *netlink_manager,
38 EventDispatcher *dispatcher,
39 const WiFiProvider::FrequencyCountList &previous_frequencies,
40 const set<uint16_t> &available_frequencies,
41 uint32_t ifindex,
42 const FractionList &fractions,
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070043 size_t min_frequencies,
44 size_t max_frequencies,
Wade Guthrief22681f2013-05-31 11:46:31 -070045 OnScanFailed on_scan_failed,
46 Metrics *metrics)
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070047 : weak_ptr_factory_(this),
48 netlink_manager_(netlink_manager),
49 dispatcher_(dispatcher),
50 frequency_list_(previous_frequencies),
Wade Guthriea60a11c2013-04-12 17:47:34 -070051 total_connections_(0),
52 total_connects_provided_(0),
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070053 total_fraction_wanted_(0.0),
54 wifi_interface_index_(ifindex),
55 ssids_(ByteString::IsLessThan),
56 fractions_(fractions),
57 min_frequencies_(min_frequencies),
58 max_frequencies_(max_frequencies),
59 on_scan_failed_(on_scan_failed),
Wade Guthrief22681f2013-05-31 11:46:31 -070060 scan_tries_left_(kScanRetryCount),
Wade Guthrie7b310a12013-05-28 15:37:13 -070061 found_error_(false),
Wade Guthrief22681f2013-05-31 11:46:31 -070062 metrics_(metrics) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070063 sort(frequency_list_.begin(), frequency_list_.end(),
64 &ScanSession::CompareFrequencyCount);
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070065 // Add to |frequency_list_| all the frequencies from |available_frequencies|
66 // that aren't in |previous_frequencies|.
Wade Guthriea60a11c2013-04-12 17:47:34 -070067 set<uint16_t> seen_frequencies;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070068 for (const auto &freq_conn : frequency_list_) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070069 seen_frequencies.insert(freq_conn.frequency);
70 total_connections_ += freq_conn.connection_count;
71 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070072 for (const auto freq : available_frequencies) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070073 if (!ContainsKey(seen_frequencies, freq)) {
74 frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0));
75 }
76 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070077
Wade Guthrie086eb1e2013-05-31 17:31:13 -070078 SLOG(WiFi, 6) << "Frequency connections vector:";
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070079 for (const auto &freq_conn : frequency_list_) {
Wade Guthrie086eb1e2013-05-31 17:31:13 -070080 SLOG(WiFi, 6) << " freq[" << freq_conn.frequency << "] = "
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070081 << freq_conn.connection_count;
82 }
Wade Guthrief22681f2013-05-31 11:46:31 -070083
Wade Guthrie7b310a12013-05-28 15:37:13 -070084 original_frequency_count_ = frequency_list_.size();
Wade Guthrief22681f2013-05-31 11:46:31 -070085 ebusy_timer_.Pause();
Wade Guthriea60a11c2013-04-12 17:47:34 -070086}
87
Wade Guthrief22681f2013-05-31 11:46:31 -070088ScanSession::~ScanSession() {
89 const int kLogLevel = 6;
Wade Guthrie7b310a12013-05-28 15:37:13 -070090 ReportResults(kLogLevel);
Wade Guthrief22681f2013-05-31 11:46:31 -070091}
Wade Guthriea60a11c2013-04-12 17:47:34 -070092
93bool ScanSession::HasMoreFrequencies() const {
94 return !frequency_list_.empty();
95}
96
97vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted,
98 size_t min_frequencies,
99 size_t max_frequencies) {
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700100 DCHECK_GE(fraction_wanted, 0);
Wade Guthriea60a11c2013-04-12 17:47:34 -0700101 total_fraction_wanted_ += fraction_wanted;
102 float total_connects_wanted = total_fraction_wanted_ * total_connections_;
103
104 vector<uint16_t> frequencies;
105 WiFiProvider::FrequencyCountList::iterator freq_connect =
106 frequency_list_.begin();
107 SLOG(WiFi, 7) << "Scanning for frequencies:";
108 while (freq_connect != frequency_list_.end()) {
109 if (frequencies.size() >= min_frequencies) {
110 if (total_connects_provided_ >= total_connects_wanted)
111 break;
112 if (frequencies.size() >= max_frequencies)
113 break;
114 }
115 uint16_t frequency = freq_connect->frequency;
116 size_t connection_count = freq_connect->connection_count;
117 total_connects_provided_ += connection_count;
118 frequencies.push_back(frequency);
119 SLOG(WiFi, 7) << " freq[" << frequency << "] = " << connection_count;
120
121 freq_connect = frequency_list_.erase(freq_connect);
122 }
123 return frequencies;
124}
125
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700126void ScanSession::InitiateScan() {
127 float fraction_wanted = kAllFrequencies;
128 if (!fractions_.empty()) {
129 fraction_wanted = fractions_.front();
130 fractions_.pop_front();
131 }
132 current_scan_frequencies_ = GetScanFrequencies(fraction_wanted,
133 min_frequencies_,
134 max_frequencies_);
135 DoScan(current_scan_frequencies_);
136}
137
138void ScanSession::ReInitiateScan() {
Wade Guthrief22681f2013-05-31 11:46:31 -0700139 ebusy_timer_.Pause();
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700140 DoScan(current_scan_frequencies_);
141}
142
143void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) {
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700144 if (scan_frequencies.empty()) {
145 LOG(INFO) << "Not sending empty frequency list";
146 return;
147 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700148 TriggerScanMessage trigger_scan;
149 trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
150 wifi_interface_index_);
151 AttributeListRefPtr frequency_list;
152 if (!trigger_scan.attributes()->GetNestedAttributeList(
153 NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) || !frequency_list) {
154 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES.";
155 }
156 trigger_scan.attributes()->SetNestedAttributeHasAValue(
157 NL80211_ATTR_SCAN_FREQUENCIES);
158
Wade Guthrie086eb1e2013-05-31 17:31:13 -0700159 SLOG(WiFi, 6) << "We have requested scan frequencies:";
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700160 string attribute_name;
161 int i = 0;
162 for (const auto freq : scan_frequencies) {
Wade Guthrie086eb1e2013-05-31 17:31:13 -0700163 SLOG(WiFi, 6) << " " << freq;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700164 attribute_name = StringPrintf("Frequency-%d", i);
165 frequency_list->CreateU32Attribute(i, attribute_name.c_str());
166 frequency_list->SetU32AttributeValue(i, freq);
167 ++i;
168 }
169
170 if (!ssids_.empty()) {
171 AttributeListRefPtr ssid_list;
172 if (!trigger_scan.attributes()->GetNestedAttributeList(
173 NL80211_ATTR_SCAN_SSIDS, &ssid_list) || !ssid_list) {
174 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_SSIDS attribute.";
175 }
176 trigger_scan.attributes()->SetNestedAttributeHasAValue(
177 NL80211_ATTR_SCAN_SSIDS);
178 int i = 0;
179 string attribute_name;
180 for (const auto &ssid : ssids_) {
181 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
182 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
183 ssid_list->SetRawAttributeValue(i, ssid);
184 ++i;
185 }
186 // Add an empty one at the end so we ask for a broadcast in addition to
187 // the specific SSIDs.
188 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
189 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
190 ssid_list->SetRawAttributeValue(i, ByteString());
191 }
Wade Guthrie7347bf22013-04-30 11:21:51 -0700192 netlink_manager_->SendNl80211Message(
193 &trigger_scan,
194 Bind(&ScanSession::OnTriggerScanResponse,
195 weak_ptr_factory_.GetWeakPtr()),
196 Bind(&ScanSession::OnTriggerScanErrorResponse,
197 weak_ptr_factory_.GetWeakPtr()));
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700198}
199
Wade Guthrie7347bf22013-04-30 11:21:51 -0700200void ScanSession::OnTriggerScanResponse(const Nl80211Message &netlink_message) {
201 LOG(WARNING) << "Didn't expect _this_ message, here:";
202 netlink_message.Print(0, 0);
203 on_scan_failed_.Run();
204 return;
205}
206
207void ScanSession::OnTriggerScanErrorResponse(
Wade Guthrie84db7ce2013-06-12 11:40:49 -0700208 NetlinkManager::AuxilliaryMessageType type,
Wade Guthrie7347bf22013-04-30 11:21:51 -0700209 const NetlinkMessage *netlink_message) {
Wade Guthrie84db7ce2013-06-12 11:40:49 -0700210 switch (type) {
211 case NetlinkManager::kErrorFromKernel: {
212 if (!netlink_message) {
213 LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
214 found_error_ = true;
215 on_scan_failed_.Run();
216 break;
217 }
218 if (netlink_message->message_type() !=
219 ErrorAckMessage::GetMessageType()) {
220 LOG(ERROR) << __func__ << ": Message failed: Not an error.";
221 found_error_ = true;
222 on_scan_failed_.Run();
223 break;
224 }
225 const ErrorAckMessage *error_ack_message =
226 dynamic_cast<const ErrorAckMessage *>(netlink_message);
227 if (error_ack_message->error()) {
228 LOG(ERROR) << __func__ << ": Message failed: "
229 << error_ack_message->ToString();
230 if (error_ack_message->error() == EBUSY) {
231 if (scan_tries_left_ == 0) {
232 LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
233 << " times and failed each time. Giving up.";
234 found_error_ = true;
235 on_scan_failed_.Run();
236 scan_tries_left_ = kScanRetryCount;
237 return;
238 }
239 --scan_tries_left_;
240 SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_
241 << " remaining after this)";
242 ebusy_timer_.Resume();
243 dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
244 weak_ptr_factory_.GetWeakPtr()),
245 kScanRetryDelayMilliseconds);
246 break;
247 }
248 found_error_ = true;
249 on_scan_failed_.Run();
250 } else {
251 SLOG(WiFi, 6) << __func__ << ": Message ACKed";
252 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700253 }
Wade Guthrie84db7ce2013-06-12 11:40:49 -0700254 break;
255
256 case NetlinkManager::kUnexpectedResponseType:
257 LOG(ERROR) << "Message not handled by regular message handler:";
258 if (netlink_message) {
259 netlink_message->Print(0, 0);
260 }
261 found_error_ = true;
262 on_scan_failed_.Run();
263 break;
264
265 case NetlinkManager::kTimeoutWaitingForResponse:
266 // This is actually expected since, in the working case, a trigger scan
267 // message gets its responses broadcast rather than unicast.
268 break;
269
270 default:
271 LOG(ERROR) << "Unexpected auxilliary message type: " << type;
272 found_error_ = true;
273 on_scan_failed_.Run();
274 break;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700275 }
276}
277
Wade Guthrie7b310a12013-05-28 15:37:13 -0700278void ScanSession::ReportResults(int log_level) {
279 SLOG(WiFi, log_level) << "------ ScanSession finished ------";
280 SLOG(WiFi, log_level) << "Scanned "
281 << original_frequency_count_ - frequency_list_.size()
282 << " frequencies (" << frequency_list_.size()
283 << " remaining)";
284 if (found_error_) {
285 SLOG(WiFi, log_level) << "ERROR encountered during scan ("
286 << current_scan_frequencies_.size() << " frequencies"
287 << " dangling - counted as scanned but, really, not)";
288 } else {
289 SLOG(WiFi, log_level) << "No error encountered during scan.";
290 }
291
Wade Guthrief22681f2013-05-31 11:46:31 -0700292 base::TimeDelta elapsed_time;
293 ebusy_timer_.GetElapsedTime(&elapsed_time);
294 if (metrics_) {
295 metrics_->SendToUMA(Metrics::kMetricWiFiScanTimeInEbusyMilliseconds,
296 elapsed_time.InMilliseconds(),
297 Metrics::kMetricTimeToScanMillisecondsMin,
298 Metrics::kMetricTimeToScanMillisecondsMax,
299 Metrics::kMetricTimeToScanMillisecondsNumBuckets);
300 }
301 SLOG(WiFi, log_level) << "Spent " << elapsed_time.InMillisecondsRoundedUp()
302 << " milliseconds waiting for EBUSY.";
303}
304
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700305void ScanSession::AddSsid(const ByteString &ssid) {
306 ssids_.insert(ssid);
307}
308
Wade Guthriea60a11c2013-04-12 17:47:34 -0700309// static
310bool ScanSession::CompareFrequencyCount(
311 const WiFiProvider::FrequencyCount &first,
312 const WiFiProvider::FrequencyCount &second) {
313 return first.connection_count > second.connection_count;
314}
315
316} // namespace shill
317