| 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 |   } | 
| Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 181 |   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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 187 | } | 
 | 188 |  | 
| Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 189 | void 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 |  | 
 | 196 | void ScanSession::OnTriggerScanErrorResponse( | 
 | 197 |     const NetlinkMessage *netlink_message) { | 
 | 198 |   if (!netlink_message) { | 
 | 199 |     LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error."; | 
| Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 200 |     on_scan_failed_.Run(); | 
 | 201 |     return; | 
 | 202 |   } | 
| Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 203 |   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 Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 208 |   const ErrorAckMessage *error_ack_message = | 
| Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 209 |       dynamic_cast<const ErrorAckMessage *>(netlink_message); | 
| Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 210 |   if (error_ack_message->error()) { | 
 | 211 |     LOG(ERROR) << __func__ << ": Message failed: " | 
 | 212 |                << error_ack_message->ToString(); | 
 | 213 |     if (error_ack_message->error() == EBUSY) { | 
| Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 214 |       if (scan_tries_left_ == 0) { | 
| Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 215 |         LOG(ERROR) << "Retried progressive scan " << kScanRetryCount | 
 | 216 |                    << " times and failed each time.  Giving up."; | 
 | 217 |         on_scan_failed_.Run(); | 
| Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 218 |         scan_tries_left_ = kScanRetryCount; | 
| Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 219 |         return; | 
 | 220 |       } | 
| Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 221 |       --scan_tries_left_; | 
 | 222 |       SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_ | 
 | 223 |                     << " remaining after this)"; | 
| Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 224 |       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 |  | 
 | 235 | void ScanSession::AddSsid(const ByteString &ssid) { | 
 | 236 |   ssids_.insert(ssid); | 
 | 237 | } | 
 | 238 |  | 
| Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 239 | // static | 
 | 240 | bool 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 |  |