1 //
2 // Copyright (C) 2013 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/wifi/scan_session.h"
18
19 #include <algorithm>
20 #include <set>
21 #include <string>
22 #include <vector>
23
24 #include <base/bind.h>
25 #include <base/memory/weak_ptr.h>
26 #include <base/stl_util.h>
27 #include <base/strings/stringprintf.h>
28
29 #include "shill/event_dispatcher.h"
30 #include "shill/logging.h"
31 #include "shill/metrics.h"
32 #include "shill/net/netlink_manager.h"
33 #include "shill/net/netlink_message.h"
34 #include "shill/net/nl80211_attribute.h"
35 #include "shill/net/nl80211_message.h"
36
37 using base::Bind;
38 using base::StringPrintf;
39 using std::set;
40 using std::string;
41 using std::vector;
42
43 namespace shill {
44
45 namespace Logging {
46 static auto kModuleLogScope = ScopeLogger::kWiFi;
ObjectID(ScanSession * s)47 static string ObjectID(ScanSession* s) { return "(scan_session)"; }
48 }
49
50 const float ScanSession::kAllFrequencies = 1.1;
51 const uint64_t ScanSession::kScanRetryDelayMilliseconds = 200; // Arbitrary.
52 const size_t ScanSession::kScanRetryCount = 50;
53
ScanSession(NetlinkManager * netlink_manager,EventDispatcher * dispatcher,const WiFiProvider::FrequencyCountList & previous_frequencies,const set<uint16_t> & available_frequencies,uint32_t ifindex,const FractionList & fractions,size_t min_frequencies,size_t max_frequencies,OnScanFailed on_scan_failed,Metrics * metrics)54 ScanSession::ScanSession(
55 NetlinkManager* netlink_manager,
56 EventDispatcher* dispatcher,
57 const WiFiProvider::FrequencyCountList& previous_frequencies,
58 const set<uint16_t>& available_frequencies,
59 uint32_t ifindex,
60 const FractionList& fractions,
61 size_t min_frequencies,
62 size_t max_frequencies,
63 OnScanFailed on_scan_failed,
64 Metrics* metrics)
65 : weak_ptr_factory_(this),
66 netlink_manager_(netlink_manager),
67 dispatcher_(dispatcher),
68 frequency_list_(previous_frequencies),
69 total_connections_(0),
70 total_connects_provided_(0),
71 total_fraction_wanted_(0.0),
72 wifi_interface_index_(ifindex),
73 ssids_(ByteString::IsLessThan),
74 fractions_(fractions),
75 min_frequencies_(min_frequencies),
76 max_frequencies_(max_frequencies),
77 on_scan_failed_(on_scan_failed),
78 scan_tries_left_(kScanRetryCount),
79 found_error_(false),
80 metrics_(metrics) {
81 sort(frequency_list_.begin(), frequency_list_.end(),
82 &ScanSession::CompareFrequencyCount);
83 // Add to |frequency_list_| all the frequencies from |available_frequencies|
84 // that aren't in |previous_frequencies|.
85 set<uint16_t> seen_frequencies;
86 for (const auto& freq_conn : frequency_list_) {
87 seen_frequencies.insert(freq_conn.frequency);
88 total_connections_ += freq_conn.connection_count;
89 }
90 for (const auto freq : available_frequencies) {
91 if (!ContainsKey(seen_frequencies, freq)) {
92 frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0));
93 }
94 }
95
96 SLOG(this, 6) << "Frequency connections vector:";
97 for (const auto& freq_conn : frequency_list_) {
98 SLOG(this, 6) << " freq[" << freq_conn.frequency << "] = "
99 << freq_conn.connection_count;
100 }
101
102 original_frequency_count_ = frequency_list_.size();
103 ebusy_timer_.Pause();
104 }
105
~ScanSession()106 ScanSession::~ScanSession() {
107 const int kLogLevel = 6;
108 ReportResults(kLogLevel);
109 }
110
HasMoreFrequencies() const111 bool ScanSession::HasMoreFrequencies() const {
112 return !frequency_list_.empty();
113 }
114
GetScanFrequencies(float fraction_wanted,size_t min_frequencies,size_t max_frequencies)115 vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted,
116 size_t min_frequencies,
117 size_t max_frequencies) {
118 DCHECK_GE(fraction_wanted, 0);
119 total_fraction_wanted_ += fraction_wanted;
120 float total_connects_wanted = total_fraction_wanted_ * total_connections_;
121
122 vector<uint16_t> frequencies;
123 WiFiProvider::FrequencyCountList::iterator freq_connect =
124 frequency_list_.begin();
125 SLOG(this, 7) << "Scanning for frequencies:";
126 while (freq_connect != frequency_list_.end()) {
127 if (frequencies.size() >= min_frequencies) {
128 if (total_connects_provided_ >= total_connects_wanted)
129 break;
130 if (frequencies.size() >= max_frequencies)
131 break;
132 }
133 uint16_t frequency = freq_connect->frequency;
134 size_t connection_count = freq_connect->connection_count;
135 total_connects_provided_ += connection_count;
136 frequencies.push_back(frequency);
137 SLOG(this, 7) << " freq[" << frequency << "] = " << connection_count;
138
139 freq_connect = frequency_list_.erase(freq_connect);
140 }
141 return frequencies;
142 }
143
InitiateScan()144 void ScanSession::InitiateScan() {
145 float fraction_wanted = kAllFrequencies;
146 if (!fractions_.empty()) {
147 fraction_wanted = fractions_.front();
148 fractions_.pop_front();
149 }
150 current_scan_frequencies_ = GetScanFrequencies(fraction_wanted,
151 min_frequencies_,
152 max_frequencies_);
153 DoScan(current_scan_frequencies_);
154 }
155
ReInitiateScan()156 void ScanSession::ReInitiateScan() {
157 ebusy_timer_.Pause();
158 DoScan(current_scan_frequencies_);
159 }
160
DoScan(const vector<uint16_t> & scan_frequencies)161 void ScanSession::DoScan(const vector<uint16_t>& scan_frequencies) {
162 if (scan_frequencies.empty()) {
163 LOG(INFO) << "Not sending empty frequency list";
164 return;
165 }
166 TriggerScanMessage trigger_scan;
167 trigger_scan.attributes()->CreateNl80211Attribute(
168 NL80211_ATTR_SCAN_FREQUENCIES, NetlinkMessage::MessageContext());
169 trigger_scan.attributes()->CreateNl80211Attribute(
170 NL80211_ATTR_SCAN_SSIDS, NetlinkMessage::MessageContext());
171 trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
172 wifi_interface_index_);
173 AttributeListRefPtr frequency_list;
174 if (!trigger_scan.attributes()->GetNestedAttributeList(
175 NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) || !frequency_list) {
176 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES.";
177 }
178 trigger_scan.attributes()->SetNestedAttributeHasAValue(
179 NL80211_ATTR_SCAN_FREQUENCIES);
180
181 SLOG(this, 6) << "We have requested scan frequencies:";
182 string attribute_name;
183 int i = 0;
184 for (const auto freq : scan_frequencies) {
185 SLOG(this, 6) << " " << freq;
186 attribute_name = StringPrintf("Frequency-%d", i);
187 frequency_list->CreateU32Attribute(i, attribute_name.c_str());
188 frequency_list->SetU32AttributeValue(i, freq);
189 ++i;
190 }
191
192 if (!ssids_.empty()) {
193 AttributeListRefPtr ssid_list;
194 if (!trigger_scan.attributes()->GetNestedAttributeList(
195 NL80211_ATTR_SCAN_SSIDS, &ssid_list) || !ssid_list) {
196 LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_SSIDS attribute.";
197 }
198 trigger_scan.attributes()->SetNestedAttributeHasAValue(
199 NL80211_ATTR_SCAN_SSIDS);
200 int i = 0;
201 string attribute_name;
202 for (const auto& ssid : ssids_) {
203 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
204 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
205 ssid_list->SetRawAttributeValue(i, ssid);
206 ++i;
207 }
208 // Add an empty one at the end so we ask for a broadcast in addition to
209 // the specific SSIDs.
210 attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i);
211 ssid_list->CreateRawAttribute(i, attribute_name.c_str());
212 ssid_list->SetRawAttributeValue(i, ByteString());
213 }
214 netlink_manager_->SendNl80211Message(
215 &trigger_scan,
216 Bind(&ScanSession::OnTriggerScanResponse,
217 weak_ptr_factory_.GetWeakPtr()),
218 Bind(&NetlinkManager::OnAckDoNothing),
219 Bind(&ScanSession::OnTriggerScanErrorResponse,
220 weak_ptr_factory_.GetWeakPtr()));
221 }
222
OnTriggerScanResponse(const Nl80211Message & netlink_message)223 void ScanSession::OnTriggerScanResponse(const Nl80211Message& netlink_message) {
224 LOG(WARNING) << "Didn't expect _this_ netlink message, here:";
225 netlink_message.Print(0, 0);
226 on_scan_failed_.Run();
227 return;
228 }
229
OnTriggerScanErrorResponse(NetlinkManager::AuxilliaryMessageType type,const NetlinkMessage * netlink_message)230 void ScanSession::OnTriggerScanErrorResponse(
231 NetlinkManager::AuxilliaryMessageType type,
232 const NetlinkMessage* netlink_message) {
233 switch (type) {
234 case NetlinkManager::kErrorFromKernel: {
235 if (!netlink_message) {
236 LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
237 found_error_ = true;
238 on_scan_failed_.Run();
239 break;
240 }
241 if (netlink_message->message_type() !=
242 ErrorAckMessage::GetMessageType()) {
243 LOG(ERROR) << __func__ << ": Message failed: Not an error.";
244 found_error_ = true;
245 on_scan_failed_.Run();
246 break;
247 }
248 const ErrorAckMessage* error_ack_message =
249 static_cast<const ErrorAckMessage*>(netlink_message);
250 if (error_ack_message->error()) {
251 LOG(ERROR) << __func__ << ": Message failed: "
252 << error_ack_message->ToString();
253 if (error_ack_message->error() == EBUSY) {
254 if (scan_tries_left_ == 0) {
255 LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
256 << " times and failed each time. Giving up.";
257 found_error_ = true;
258 on_scan_failed_.Run();
259 scan_tries_left_ = kScanRetryCount;
260 return;
261 }
262 --scan_tries_left_;
263 SLOG(this, 3) << __func__ << " - trying again (" << scan_tries_left_
264 << " remaining after this)";
265 ebusy_timer_.Resume();
266 dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
267 weak_ptr_factory_.GetWeakPtr()),
268 kScanRetryDelayMilliseconds);
269 break;
270 }
271 found_error_ = true;
272 on_scan_failed_.Run();
273 } else {
274 SLOG(this, 6) << __func__ << ": Message ACKed";
275 }
276 }
277 break;
278
279 case NetlinkManager::kUnexpectedResponseType:
280 LOG(ERROR) << "Message not handled by regular message handler:";
281 if (netlink_message) {
282 netlink_message->Print(0, 0);
283 }
284 found_error_ = true;
285 on_scan_failed_.Run();
286 break;
287
288 case NetlinkManager::kTimeoutWaitingForResponse:
289 // This is actually expected since, in the working case, a trigger scan
290 // message gets its responses broadcast rather than unicast.
291 break;
292
293 default:
294 LOG(ERROR) << "Unexpected auxiliary message type: " << type;
295 found_error_ = true;
296 on_scan_failed_.Run();
297 break;
298 }
299 }
300
ReportResults(int log_level)301 void ScanSession::ReportResults(int log_level) {
302 SLOG(this, log_level) << "------ ScanSession finished ------";
303 SLOG(this, log_level) << "Scanned "
304 << original_frequency_count_ - frequency_list_.size()
305 << " frequencies (" << frequency_list_.size()
306 << " remaining)";
307 if (found_error_) {
308 SLOG(this, log_level) << "ERROR encountered during scan ("
309 << current_scan_frequencies_.size() << " frequencies"
310 << " dangling - counted as scanned but, really, not)";
311 } else {
312 SLOG(this, log_level) << "No error encountered during scan.";
313 }
314
315 base::TimeDelta elapsed_time;
316 ebusy_timer_.GetElapsedTime(&elapsed_time);
317 if (metrics_) {
318 metrics_->SendToUMA(Metrics::kMetricWiFiScanTimeInEbusyMilliseconds,
319 elapsed_time.InMilliseconds(),
320 Metrics::kMetricTimeToScanMillisecondsMin,
321 Metrics::kMetricTimeToScanMillisecondsMax,
322 Metrics::kMetricTimeToScanMillisecondsNumBuckets);
323 }
324 SLOG(this, log_level) << "Spent " << elapsed_time.InMillisecondsRoundedUp()
325 << " milliseconds waiting for EBUSY.";
326 }
327
AddSsid(const ByteString & ssid)328 void ScanSession::AddSsid(const ByteString& ssid) {
329 ssids_.insert(ssid);
330 }
331
332 // static
CompareFrequencyCount(const WiFiProvider::FrequencyCount & first,const WiFiProvider::FrequencyCount & second)333 bool ScanSession::CompareFrequencyCount(
334 const WiFiProvider::FrequencyCount& first,
335 const WiFiProvider::FrequencyCount& second) {
336 return first.connection_count > second.connection_count;
337 }
338
339 } // namespace shill
340
341