blob: 67f21311a9b5666681e0c6164854bfef6e62d2d7 [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,
42 int min_frequencies,
43 int max_frequencies,
44 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),
58 scan_tries_left_(kScanRetryCount + 1) {
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) {
133 TriggerScanMessage trigger_scan;
134 trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
135 wifi_interface_index_);
136 AttributeListRefPtr frequency_list;
137 if (!trigger_scan.attributes()->GetNestedAttributeList(
138 NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) || !frequency_list) {
139 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES.";
140 }
141 trigger_scan.attributes()->SetNestedAttributeHasAValue(
142 NL80211_ATTR_SCAN_FREQUENCIES);
143
144 SLOG(WiFi, 7) << "We have requested scan frequencies:";
145 string attribute_name;
146 int i = 0;
147 for (const auto freq : scan_frequencies) {
148 SLOG(WiFi, 7) << " " << freq;
149 attribute_name = StringPrintf("Frequency-%d", i);
150 frequency_list->CreateU32Attribute(i, attribute_name.c_str());
151 frequency_list->SetU32AttributeValue(i, freq);
152 ++i;
153 }
154
155 if (!ssids_.empty()) {
156 AttributeListRefPtr ssid_list;
157 if (!trigger_scan.attributes()->GetNestedAttributeList(
158 NL80211_ATTR_SCAN_SSIDS, &ssid_list) || !ssid_list) {
159 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_SSIDS attribute.";
160 }
161 trigger_scan.attributes()->SetNestedAttributeHasAValue(
162 NL80211_ATTR_SCAN_SSIDS);
163 int i = 0;
164 string attribute_name;
165 for (const auto &ssid : ssids_) {
166 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
167 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
168 ssid_list->SetRawAttributeValue(i, ssid);
169 ++i;
170 }
171 // Add an empty one at the end so we ask for a broadcast in addition to
172 // the specific SSIDs.
173 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
174 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
175 ssid_list->SetRawAttributeValue(i, ByteString());
176 }
177 netlink_manager_->SendMessage(&trigger_scan,
178 Bind(&ScanSession::OnTriggerScanResponse,
179 weak_ptr_factory_.GetWeakPtr()));
180}
181
182void ScanSession::OnTriggerScanResponse(const NetlinkMessage &netlink_message) {
183 if (netlink_message.message_type() != NLMSG_ERROR) {
184 LOG(WARNING) << "Didn't expect _this_ message, here:";
185 netlink_message.Print(0, 0);
186 on_scan_failed_.Run();
187 return;
188 }
189
190 const ErrorAckMessage *error_ack_message =
191 dynamic_cast<const ErrorAckMessage *>(&netlink_message);
192 if (error_ack_message->error()) {
193 LOG(ERROR) << __func__ << ": Message failed: "
194 << error_ack_message->ToString();
195 if (error_ack_message->error() == EBUSY) {
196 if (--scan_tries_left_ == 0) {
197 LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
198 << " times and failed each time. Giving up.";
199 on_scan_failed_.Run();
200 scan_tries_left_ = kScanRetryCount + 1;
201 return;
202 }
203 SLOG(WiFi, 3) << __func__ << " - trying again";
204 dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
205 weak_ptr_factory_.GetWeakPtr()),
206 kScanRetryDelayMilliseconds);
207 return;
208 }
209 on_scan_failed_.Run();
210 } else {
211 SLOG(WiFi, 6) << __func__ << ": Message ACKed";
212 }
213}
214
215void ScanSession::AddSsid(const ByteString &ssid) {
216 ssids_.insert(ssid);
217}
218
Wade Guthriea60a11c2013-04-12 17:47:34 -0700219// static
220bool ScanSession::CompareFrequencyCount(
221 const WiFiProvider::FrequencyCount &first,
222 const WiFiProvider::FrequencyCount &second) {
223 return first.connection_count > second.connection_count;
224}
225
226} // namespace shill
227