blob: db7d1137bfe1de10eafb8a34eab3bb1a9b913f3b [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 Guthrieb9c3feb2013-04-25 16:31:19 -070019#include "shill/netlink_manager.h"
20#include "shill/nl80211_attribute.h"
21#include "shill/nl80211_message.h"
Wade Guthriea60a11c2013-04-12 17:47:34 -070022
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070023using base::Bind;
24using base::StringPrintf;
Wade Guthriea60a11c2013-04-12 17:47:34 -070025using std::set;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070026using std::string;
Wade Guthriea60a11c2013-04-12 17:47:34 -070027using std::vector;
28
29namespace shill {
30
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070031const float ScanSession::kAllFrequencies = 1.1;
32const uint64_t ScanSession::kScanRetryDelayMilliseconds = 100; // Arbitrary.
33const size_t ScanSession::kScanRetryCount = 10;
34
Wade Guthriea60a11c2013-04-12 17:47:34 -070035ScanSession::ScanSession(
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070036 NetlinkManager *netlink_manager,
37 EventDispatcher *dispatcher,
38 const WiFiProvider::FrequencyCountList &previous_frequencies,
39 const set<uint16_t> &available_frequencies,
40 uint32_t ifindex,
41 const FractionList &fractions,
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070042 size_t min_frequencies,
43 size_t max_frequencies,
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070044 OnScanFailed on_scan_failed)
45 : weak_ptr_factory_(this),
46 netlink_manager_(netlink_manager),
47 dispatcher_(dispatcher),
48 frequency_list_(previous_frequencies),
Wade Guthriea60a11c2013-04-12 17:47:34 -070049 total_connections_(0),
50 total_connects_provided_(0),
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070051 total_fraction_wanted_(0.0),
52 wifi_interface_index_(ifindex),
53 ssids_(ByteString::IsLessThan),
54 fractions_(fractions),
55 min_frequencies_(min_frequencies),
56 max_frequencies_(max_frequencies),
57 on_scan_failed_(on_scan_failed),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070058 scan_tries_left_(kScanRetryCount) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070059 sort(frequency_list_.begin(), frequency_list_.end(),
60 &ScanSession::CompareFrequencyCount);
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070061 // Add to |frequency_list_| all the frequencies from |available_frequencies|
62 // that aren't in |previous_frequencies|.
Wade Guthriea60a11c2013-04-12 17:47:34 -070063 set<uint16_t> seen_frequencies;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070064 for (const auto &freq_conn : frequency_list_) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070065 seen_frequencies.insert(freq_conn.frequency);
66 total_connections_ += freq_conn.connection_count;
67 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070068 for (const auto freq : available_frequencies) {
Wade Guthriea60a11c2013-04-12 17:47:34 -070069 if (!ContainsKey(seen_frequencies, freq)) {
70 frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0));
71 }
72 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070073
74 SLOG(WiFi, 7) << "Frequency connections vector:";
75 for (const auto &freq_conn : frequency_list_) {
76 SLOG(WiFi, 7) << " freq[" << freq_conn.frequency << "] = "
77 << freq_conn.connection_count;
78 }
Wade Guthriea60a11c2013-04-12 17:47:34 -070079}
80
81ScanSession::~ScanSession() {}
82
83bool ScanSession::HasMoreFrequencies() const {
84 return !frequency_list_.empty();
85}
86
87vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted,
88 size_t min_frequencies,
89 size_t max_frequencies) {
Wade Guthrieb9c3feb2013-04-25 16:31:19 -070090 DCHECK_GE(fraction_wanted, 0);
Wade Guthriea60a11c2013-04-12 17:47:34 -070091 total_fraction_wanted_ += fraction_wanted;
92 float total_connects_wanted = total_fraction_wanted_ * total_connections_;
93
94 vector<uint16_t> frequencies;
95 WiFiProvider::FrequencyCountList::iterator freq_connect =
96 frequency_list_.begin();
97 SLOG(WiFi, 7) << "Scanning for frequencies:";
98 while (freq_connect != frequency_list_.end()) {
99 if (frequencies.size() >= min_frequencies) {
100 if (total_connects_provided_ >= total_connects_wanted)
101 break;
102 if (frequencies.size() >= max_frequencies)
103 break;
104 }
105 uint16_t frequency = freq_connect->frequency;
106 size_t connection_count = freq_connect->connection_count;
107 total_connects_provided_ += connection_count;
108 frequencies.push_back(frequency);
109 SLOG(WiFi, 7) << " freq[" << frequency << "] = " << connection_count;
110
111 freq_connect = frequency_list_.erase(freq_connect);
112 }
113 return frequencies;
114}
115
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700116void ScanSession::InitiateScan() {
117 float fraction_wanted = kAllFrequencies;
118 if (!fractions_.empty()) {
119 fraction_wanted = fractions_.front();
120 fractions_.pop_front();
121 }
122 current_scan_frequencies_ = GetScanFrequencies(fraction_wanted,
123 min_frequencies_,
124 max_frequencies_);
125 DoScan(current_scan_frequencies_);
126}
127
128void ScanSession::ReInitiateScan() {
129 DoScan(current_scan_frequencies_);
130}
131
132void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) {
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700133 if (scan_frequencies.empty()) {
134 LOG(INFO) << "Not sending empty frequency list";
135 return;
136 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700137 TriggerScanMessage trigger_scan;
138 trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
139 wifi_interface_index_);
140 AttributeListRefPtr frequency_list;
141 if (!trigger_scan.attributes()->GetNestedAttributeList(
142 NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) || !frequency_list) {
143 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES.";
144 }
145 trigger_scan.attributes()->SetNestedAttributeHasAValue(
146 NL80211_ATTR_SCAN_FREQUENCIES);
147
148 SLOG(WiFi, 7) << "We have requested scan frequencies:";
149 string attribute_name;
150 int i = 0;
151 for (const auto freq : scan_frequencies) {
152 SLOG(WiFi, 7) << " " << freq;
153 attribute_name = StringPrintf("Frequency-%d", i);
154 frequency_list->CreateU32Attribute(i, attribute_name.c_str());
155 frequency_list->SetU32AttributeValue(i, freq);
156 ++i;
157 }
158
159 if (!ssids_.empty()) {
160 AttributeListRefPtr ssid_list;
161 if (!trigger_scan.attributes()->GetNestedAttributeList(
162 NL80211_ATTR_SCAN_SSIDS, &ssid_list) || !ssid_list) {
163 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_SSIDS attribute.";
164 }
165 trigger_scan.attributes()->SetNestedAttributeHasAValue(
166 NL80211_ATTR_SCAN_SSIDS);
167 int i = 0;
168 string attribute_name;
169 for (const auto &ssid : ssids_) {
170 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
171 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
172 ssid_list->SetRawAttributeValue(i, ssid);
173 ++i;
174 }
175 // Add an empty one at the end so we ask for a broadcast in addition to
176 // the specific SSIDs.
177 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
178 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
179 ssid_list->SetRawAttributeValue(i, ByteString());
180 }
Wade Guthrie7347bf22013-04-30 11:21:51 -0700181 netlink_manager_->SendNl80211Message(
182 &trigger_scan,
183 Bind(&ScanSession::OnTriggerScanResponse,
184 weak_ptr_factory_.GetWeakPtr()),
185 Bind(&ScanSession::OnTriggerScanErrorResponse,
186 weak_ptr_factory_.GetWeakPtr()));
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700187}
188
Wade Guthrie7347bf22013-04-30 11:21:51 -0700189void ScanSession::OnTriggerScanResponse(const Nl80211Message &netlink_message) {
190 LOG(WARNING) << "Didn't expect _this_ message, here:";
191 netlink_message.Print(0, 0);
192 on_scan_failed_.Run();
193 return;
194}
195
196void ScanSession::OnTriggerScanErrorResponse(
197 const NetlinkMessage *netlink_message) {
198 if (!netlink_message) {
199 LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700200 on_scan_failed_.Run();
201 return;
202 }
Wade Guthrie7347bf22013-04-30 11:21:51 -0700203 if (netlink_message->message_type() != ErrorAckMessage::GetMessageType()) {
204 LOG(ERROR) << __func__ << ": Message failed: Not an error.";
205 on_scan_failed_.Run();
206 return;
207 }
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700208 const ErrorAckMessage *error_ack_message =
Wade Guthrie7347bf22013-04-30 11:21:51 -0700209 dynamic_cast<const ErrorAckMessage *>(netlink_message);
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700210 if (error_ack_message->error()) {
211 LOG(ERROR) << __func__ << ": Message failed: "
212 << error_ack_message->ToString();
213 if (error_ack_message->error() == EBUSY) {
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700214 if (scan_tries_left_ == 0) {
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700215 LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
216 << " times and failed each time. Giving up.";
217 on_scan_failed_.Run();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700218 scan_tries_left_ = kScanRetryCount;
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700219 return;
220 }
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700221 --scan_tries_left_;
222 SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_
223 << " remaining after this)";
Wade Guthrieb9c3feb2013-04-25 16:31:19 -0700224 dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
225 weak_ptr_factory_.GetWeakPtr()),
226 kScanRetryDelayMilliseconds);
227 return;
228 }
229 on_scan_failed_.Run();
230 } else {
231 SLOG(WiFi, 6) << __func__ << ": Message ACKed";
232 }
233}
234
235void ScanSession::AddSsid(const ByteString &ssid) {
236 ssids_.insert(ssid);
237}
238
Wade Guthriea60a11c2013-04-12 17:47:34 -0700239// static
240bool ScanSession::CompareFrequencyCount(
241 const WiFiProvider::FrequencyCount &first,
242 const WiFiProvider::FrequencyCount &second) {
243 return first.connection_count > second.connection_count;
244}
245
246} // namespace shill
247