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 | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 19 | #include "shill/netlink_manager.h" |
| 20 | #include "shill/nl80211_attribute.h" |
| 21 | #include "shill/nl80211_message.h" |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 22 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 23 | using base::Bind; |
| 24 | using base::StringPrintf; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 25 | using std::set; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 26 | using std::string; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 27 | using std::vector; |
| 28 | |
| 29 | namespace shill { |
| 30 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 31 | const float ScanSession::kAllFrequencies = 1.1; |
| 32 | const uint64_t ScanSession::kScanRetryDelayMilliseconds = 100; // Arbitrary. |
| 33 | const size_t ScanSession::kScanRetryCount = 10; |
| 34 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 35 | ScanSession::ScanSession( |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 36 | 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 Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 42 | size_t min_frequencies, |
| 43 | size_t max_frequencies, |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 44 | OnScanFailed on_scan_failed) |
| 45 | : weak_ptr_factory_(this), |
| 46 | netlink_manager_(netlink_manager), |
| 47 | dispatcher_(dispatcher), |
| 48 | frequency_list_(previous_frequencies), |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 49 | total_connections_(0), |
| 50 | total_connects_provided_(0), |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 51 | 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 Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 58 | scan_tries_left_(kScanRetryCount) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 59 | sort(frequency_list_.begin(), frequency_list_.end(), |
| 60 | &ScanSession::CompareFrequencyCount); |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 61 | // Add to |frequency_list_| all the frequencies from |available_frequencies| |
| 62 | // that aren't in |previous_frequencies|. |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 63 | set<uint16_t> seen_frequencies; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 64 | for (const auto &freq_conn : frequency_list_) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 65 | seen_frequencies.insert(freq_conn.frequency); |
| 66 | total_connections_ += freq_conn.connection_count; |
| 67 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 68 | for (const auto freq : available_frequencies) { |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 69 | if (!ContainsKey(seen_frequencies, freq)) { |
| 70 | frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0)); |
| 71 | } |
| 72 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 73 | |
| 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 Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | ScanSession::~ScanSession() {} |
| 82 | |
| 83 | bool ScanSession::HasMoreFrequencies() const { |
| 84 | return !frequency_list_.empty(); |
| 85 | } |
| 86 | |
| 87 | vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted, |
| 88 | size_t min_frequencies, |
| 89 | size_t max_frequencies) { |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 90 | DCHECK_GE(fraction_wanted, 0); |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 91 | 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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 116 | void 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 | |
| 128 | void ScanSession::ReInitiateScan() { |
| 129 | DoScan(current_scan_frequencies_); |
| 130 | } |
| 131 | |
| 132 | void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) { |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 133 | if (scan_frequencies.empty()) { |
| 134 | LOG(INFO) << "Not sending empty frequency list"; |
| 135 | return; |
| 136 | } |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 137 | 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 | } |
| 181 | netlink_manager_->SendMessage(&trigger_scan, |
| 182 | Bind(&ScanSession::OnTriggerScanResponse, |
| 183 | weak_ptr_factory_.GetWeakPtr())); |
| 184 | } |
| 185 | |
| 186 | void ScanSession::OnTriggerScanResponse(const NetlinkMessage &netlink_message) { |
| 187 | if (netlink_message.message_type() != NLMSG_ERROR) { |
| 188 | LOG(WARNING) << "Didn't expect _this_ message, here:"; |
| 189 | netlink_message.Print(0, 0); |
| 190 | on_scan_failed_.Run(); |
| 191 | return; |
| 192 | } |
| 193 | |
| 194 | const ErrorAckMessage *error_ack_message = |
| 195 | dynamic_cast<const ErrorAckMessage *>(&netlink_message); |
| 196 | if (error_ack_message->error()) { |
| 197 | LOG(ERROR) << __func__ << ": Message failed: " |
| 198 | << error_ack_message->ToString(); |
| 199 | if (error_ack_message->error() == EBUSY) { |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 200 | if (scan_tries_left_ == 0) { |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 201 | LOG(ERROR) << "Retried progressive scan " << kScanRetryCount |
| 202 | << " times and failed each time. Giving up."; |
| 203 | on_scan_failed_.Run(); |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 204 | scan_tries_left_ = kScanRetryCount; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 205 | return; |
| 206 | } |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 207 | --scan_tries_left_; |
| 208 | SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_ |
| 209 | << " remaining after this)"; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 210 | dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan, |
| 211 | weak_ptr_factory_.GetWeakPtr()), |
| 212 | kScanRetryDelayMilliseconds); |
| 213 | return; |
| 214 | } |
| 215 | on_scan_failed_.Run(); |
| 216 | } else { |
| 217 | SLOG(WiFi, 6) << __func__ << ": Message ACKed"; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | void ScanSession::AddSsid(const ByteString &ssid) { |
| 222 | ssids_.insert(ssid); |
| 223 | } |
| 224 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 225 | // static |
| 226 | bool ScanSession::CompareFrequencyCount( |
| 227 | const WiFiProvider::FrequencyCount &first, |
| 228 | const WiFiProvider::FrequencyCount &second) { |
| 229 | return first.connection_count > second.connection_count; |
| 230 | } |
| 231 | |
| 232 | } // namespace shill |
| 233 | |