Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 1 | // 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 9 | #include <string> |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 10 | #include <vector> |
| 11 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 12 | #include <base/bind.h> |
| 13 | #include <base/memory/weak_ptr.h> |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 14 | #include <base/stl_util.h> |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 15 | #include <base/stringprintf.h> |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 16 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 17 | #include "shill/event_dispatcher.h" |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 18 | #include "shill/logging.h" |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 19 | #include "shill/metrics.h" |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 20 | #include "shill/netlink_manager.h" |
| 21 | #include "shill/nl80211_attribute.h" |
| 22 | #include "shill/nl80211_message.h" |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 23 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 24 | using base::Bind; |
| 25 | using base::StringPrintf; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 26 | using std::set; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 27 | using std::string; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 28 | using std::vector; |
| 29 | |
| 30 | namespace shill { |
| 31 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 32 | const float ScanSession::kAllFrequencies = 1.1; |
Wade Guthrie | 086eb1e | 2013-05-31 17:31:13 -0700 | [diff] [blame] | 33 | const uint64_t ScanSession::kScanRetryDelayMilliseconds = 200; // Arbitrary. |
| 34 | const size_t ScanSession::kScanRetryCount = 50; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 35 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 36 | ScanSession::ScanSession( |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 37 | 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 Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 43 | size_t min_frequencies, |
| 44 | size_t max_frequencies, |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 45 | OnScanFailed on_scan_failed, |
| 46 | Metrics *metrics) |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 47 | : weak_ptr_factory_(this), |
| 48 | netlink_manager_(netlink_manager), |
| 49 | dispatcher_(dispatcher), |
| 50 | frequency_list_(previous_frequencies), |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 51 | total_connections_(0), |
| 52 | total_connects_provided_(0), |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 53 | 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 Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 60 | scan_tries_left_(kScanRetryCount), |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 61 | found_error_(false), |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 62 | metrics_(metrics) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 63 | sort(frequency_list_.begin(), frequency_list_.end(), |
| 64 | &ScanSession::CompareFrequencyCount); |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 65 | // Add to |frequency_list_| all the frequencies from |available_frequencies| |
| 66 | // that aren't in |previous_frequencies|. |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 67 | set<uint16_t> seen_frequencies; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 68 | for (const auto &freq_conn : frequency_list_) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 69 | seen_frequencies.insert(freq_conn.frequency); |
| 70 | total_connections_ += freq_conn.connection_count; |
| 71 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 72 | for (const auto freq : available_frequencies) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 73 | if (!ContainsKey(seen_frequencies, freq)) { |
| 74 | frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0)); |
| 75 | } |
| 76 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 77 | |
Wade Guthrie | 086eb1e | 2013-05-31 17:31:13 -0700 | [diff] [blame] | 78 | SLOG(WiFi, 6) << "Frequency connections vector:"; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 79 | for (const auto &freq_conn : frequency_list_) { |
Wade Guthrie | 086eb1e | 2013-05-31 17:31:13 -0700 | [diff] [blame] | 80 | SLOG(WiFi, 6) << " freq[" << freq_conn.frequency << "] = " |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 81 | << freq_conn.connection_count; |
| 82 | } |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 83 | |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 84 | original_frequency_count_ = frequency_list_.size(); |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 85 | ebusy_timer_.Pause(); |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 86 | } |
| 87 | |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 88 | ScanSession::~ScanSession() { |
| 89 | const int kLogLevel = 6; |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 90 | ReportResults(kLogLevel); |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 91 | } |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 92 | |
| 93 | bool ScanSession::HasMoreFrequencies() const { |
| 94 | return !frequency_list_.empty(); |
| 95 | } |
| 96 | |
| 97 | vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted, |
| 98 | size_t min_frequencies, |
| 99 | size_t max_frequencies) { |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 100 | DCHECK_GE(fraction_wanted, 0); |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 101 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 126 | void 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 | |
| 138 | void ScanSession::ReInitiateScan() { |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 139 | ebusy_timer_.Pause(); |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 140 | DoScan(current_scan_frequencies_); |
| 141 | } |
| 142 | |
| 143 | void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) { |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 144 | if (scan_frequencies.empty()) { |
| 145 | LOG(INFO) << "Not sending empty frequency list"; |
| 146 | return; |
| 147 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 148 | 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 Guthrie | 086eb1e | 2013-05-31 17:31:13 -0700 | [diff] [blame] | 159 | SLOG(WiFi, 6) << "We have requested scan frequencies:"; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 160 | string attribute_name; |
| 161 | int i = 0; |
| 162 | for (const auto freq : scan_frequencies) { |
Wade Guthrie | 086eb1e | 2013-05-31 17:31:13 -0700 | [diff] [blame] | 163 | SLOG(WiFi, 6) << " " << freq; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 164 | 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 Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 192 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 198 | } |
| 199 | |
Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 200 | void 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 | |
| 207 | void ScanSession::OnTriggerScanErrorResponse( |
Wade Guthrie | 84db7ce | 2013-06-12 11:40:49 -0700 | [diff] [blame] | 208 | NetlinkManager::AuxilliaryMessageType type, |
Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 209 | const NetlinkMessage *netlink_message) { |
Wade Guthrie | 84db7ce | 2013-06-12 11:40:49 -0700 | [diff] [blame] | 210 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 253 | } |
Wade Guthrie | 84db7ce | 2013-06-12 11:40:49 -0700 | [diff] [blame] | 254 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 275 | } |
| 276 | } |
| 277 | |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 278 | void 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 Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 292 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 305 | void ScanSession::AddSsid(const ByteString &ssid) { |
| 306 | ssids_.insert(ssid); |
| 307 | } |
| 308 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 309 | // static |
| 310 | bool 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 | |