// // Copyright (C) 2013 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/wifi/scan_session.h" #include #include #include #include #include #include #include #include #include "shill/event_dispatcher.h" #include "shill/logging.h" #include "shill/metrics.h" #include "shill/net/netlink_manager.h" #include "shill/net/netlink_message.h" #include "shill/net/nl80211_attribute.h" #include "shill/net/nl80211_message.h" using base::Bind; using base::StringPrintf; using std::set; using std::string; using std::vector; namespace shill { namespace Logging { static auto kModuleLogScope = ScopeLogger::kWiFi; static string ObjectID(ScanSession* s) { return "(scan_session)"; } } const float ScanSession::kAllFrequencies = 1.1; const uint64_t ScanSession::kScanRetryDelayMilliseconds = 200; // Arbitrary. const size_t ScanSession::kScanRetryCount = 50; ScanSession::ScanSession( NetlinkManager* netlink_manager, EventDispatcher* dispatcher, const WiFiProvider::FrequencyCountList& previous_frequencies, const set& available_frequencies, uint32_t ifindex, const FractionList& fractions, size_t min_frequencies, size_t max_frequencies, OnScanFailed on_scan_failed, Metrics* metrics) : weak_ptr_factory_(this), netlink_manager_(netlink_manager), dispatcher_(dispatcher), frequency_list_(previous_frequencies), total_connections_(0), total_connects_provided_(0), total_fraction_wanted_(0.0), wifi_interface_index_(ifindex), ssids_(ByteString::IsLessThan), fractions_(fractions), min_frequencies_(min_frequencies), max_frequencies_(max_frequencies), on_scan_failed_(on_scan_failed), scan_tries_left_(kScanRetryCount), found_error_(false), metrics_(metrics) { sort(frequency_list_.begin(), frequency_list_.end(), &ScanSession::CompareFrequencyCount); // Add to |frequency_list_| all the frequencies from |available_frequencies| // that aren't in |previous_frequencies|. set seen_frequencies; for (const auto& freq_conn : frequency_list_) { seen_frequencies.insert(freq_conn.frequency); total_connections_ += freq_conn.connection_count; } for (const auto freq : available_frequencies) { if (!ContainsKey(seen_frequencies, freq)) { frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0)); } } SLOG(this, 6) << "Frequency connections vector:"; for (const auto& freq_conn : frequency_list_) { SLOG(this, 6) << " freq[" << freq_conn.frequency << "] = " << freq_conn.connection_count; } original_frequency_count_ = frequency_list_.size(); ebusy_timer_.Pause(); } ScanSession::~ScanSession() { const int kLogLevel = 6; ReportResults(kLogLevel); } bool ScanSession::HasMoreFrequencies() const { return !frequency_list_.empty(); } vector ScanSession::GetScanFrequencies(float fraction_wanted, size_t min_frequencies, size_t max_frequencies) { DCHECK_GE(fraction_wanted, 0); total_fraction_wanted_ += fraction_wanted; float total_connects_wanted = total_fraction_wanted_ * total_connections_; vector frequencies; WiFiProvider::FrequencyCountList::iterator freq_connect = frequency_list_.begin(); SLOG(this, 7) << "Scanning for frequencies:"; while (freq_connect != frequency_list_.end()) { if (frequencies.size() >= min_frequencies) { if (total_connects_provided_ >= total_connects_wanted) break; if (frequencies.size() >= max_frequencies) break; } uint16_t frequency = freq_connect->frequency; size_t connection_count = freq_connect->connection_count; total_connects_provided_ += connection_count; frequencies.push_back(frequency); SLOG(this, 7) << " freq[" << frequency << "] = " << connection_count; freq_connect = frequency_list_.erase(freq_connect); } return frequencies; } void ScanSession::InitiateScan() { float fraction_wanted = kAllFrequencies; if (!fractions_.empty()) { fraction_wanted = fractions_.front(); fractions_.pop_front(); } current_scan_frequencies_ = GetScanFrequencies(fraction_wanted, min_frequencies_, max_frequencies_); DoScan(current_scan_frequencies_); } void ScanSession::ReInitiateScan() { ebusy_timer_.Pause(); DoScan(current_scan_frequencies_); } void ScanSession::DoScan(const vector& scan_frequencies) { if (scan_frequencies.empty()) { LOG(INFO) << "Not sending empty frequency list"; return; } TriggerScanMessage trigger_scan; trigger_scan.attributes()->CreateNl80211Attribute( NL80211_ATTR_SCAN_FREQUENCIES, NetlinkMessage::MessageContext()); trigger_scan.attributes()->CreateNl80211Attribute( NL80211_ATTR_SCAN_SSIDS, NetlinkMessage::MessageContext()); trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX, wifi_interface_index_); AttributeListRefPtr frequency_list; if (!trigger_scan.attributes()->GetNestedAttributeList( NL80211_ATTR_SCAN_FREQUENCIES, &frequency_list) || !frequency_list) { LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_FREQUENCIES."; } trigger_scan.attributes()->SetNestedAttributeHasAValue( NL80211_ATTR_SCAN_FREQUENCIES); SLOG(this, 6) << "We have requested scan frequencies:"; string attribute_name; int i = 0; for (const auto freq : scan_frequencies) { SLOG(this, 6) << " " << freq; attribute_name = StringPrintf("Frequency-%d", i); frequency_list->CreateU32Attribute(i, attribute_name.c_str()); frequency_list->SetU32AttributeValue(i, freq); ++i; } if (!ssids_.empty()) { AttributeListRefPtr ssid_list; if (!trigger_scan.attributes()->GetNestedAttributeList( NL80211_ATTR_SCAN_SSIDS, &ssid_list) || !ssid_list) { LOG(FATAL) << "Couldn't get NL80211_ATTR_SCAN_SSIDS attribute."; } trigger_scan.attributes()->SetNestedAttributeHasAValue( NL80211_ATTR_SCAN_SSIDS); int i = 0; string attribute_name; for (const auto& ssid : ssids_) { attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i); ssid_list->CreateRawAttribute(i, attribute_name.c_str()); ssid_list->SetRawAttributeValue(i, ssid); ++i; } // Add an empty one at the end so we ask for a broadcast in addition to // the specific SSIDs. attribute_name = StringPrintf("NL80211_ATTR_SSID_%d", i); ssid_list->CreateRawAttribute(i, attribute_name.c_str()); ssid_list->SetRawAttributeValue(i, ByteString()); } netlink_manager_->SendNl80211Message( &trigger_scan, Bind(&ScanSession::OnTriggerScanResponse, weak_ptr_factory_.GetWeakPtr()), Bind(&NetlinkManager::OnAckDoNothing), Bind(&ScanSession::OnTriggerScanErrorResponse, weak_ptr_factory_.GetWeakPtr())); } void ScanSession::OnTriggerScanResponse(const Nl80211Message& netlink_message) { LOG(WARNING) << "Didn't expect _this_ netlink message, here:"; netlink_message.Print(0, 0); on_scan_failed_.Run(); return; } void ScanSession::OnTriggerScanErrorResponse( NetlinkManager::AuxilliaryMessageType type, const NetlinkMessage* netlink_message) { switch (type) { case NetlinkManager::kErrorFromKernel: { if (!netlink_message) { LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error."; found_error_ = true; on_scan_failed_.Run(); break; } if (netlink_message->message_type() != ErrorAckMessage::GetMessageType()) { LOG(ERROR) << __func__ << ": Message failed: Not an error."; found_error_ = true; on_scan_failed_.Run(); break; } const ErrorAckMessage* error_ack_message = static_cast(netlink_message); if (error_ack_message->error()) { LOG(ERROR) << __func__ << ": Message failed: " << error_ack_message->ToString(); if (error_ack_message->error() == EBUSY) { if (scan_tries_left_ == 0) { LOG(ERROR) << "Retried progressive scan " << kScanRetryCount << " times and failed each time. Giving up."; found_error_ = true; on_scan_failed_.Run(); scan_tries_left_ = kScanRetryCount; return; } --scan_tries_left_; SLOG(this, 3) << __func__ << " - trying again (" << scan_tries_left_ << " remaining after this)"; ebusy_timer_.Resume(); dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan, weak_ptr_factory_.GetWeakPtr()), kScanRetryDelayMilliseconds); break; } found_error_ = true; on_scan_failed_.Run(); } else { SLOG(this, 6) << __func__ << ": Message ACKed"; } } break; case NetlinkManager::kUnexpectedResponseType: LOG(ERROR) << "Message not handled by regular message handler:"; if (netlink_message) { netlink_message->Print(0, 0); } found_error_ = true; on_scan_failed_.Run(); break; case NetlinkManager::kTimeoutWaitingForResponse: // This is actually expected since, in the working case, a trigger scan // message gets its responses broadcast rather than unicast. break; default: LOG(ERROR) << "Unexpected auxiliary message type: " << type; found_error_ = true; on_scan_failed_.Run(); break; } } void ScanSession::ReportResults(int log_level) { SLOG(this, log_level) << "------ ScanSession finished ------"; SLOG(this, log_level) << "Scanned " << original_frequency_count_ - frequency_list_.size() << " frequencies (" << frequency_list_.size() << " remaining)"; if (found_error_) { SLOG(this, log_level) << "ERROR encountered during scan (" << current_scan_frequencies_.size() << " frequencies" << " dangling - counted as scanned but, really, not)"; } else { SLOG(this, log_level) << "No error encountered during scan."; } base::TimeDelta elapsed_time; ebusy_timer_.GetElapsedTime(&elapsed_time); if (metrics_) { metrics_->SendToUMA(Metrics::kMetricWiFiScanTimeInEbusyMilliseconds, elapsed_time.InMilliseconds(), Metrics::kMetricTimeToScanMillisecondsMin, Metrics::kMetricTimeToScanMillisecondsMax, Metrics::kMetricTimeToScanMillisecondsNumBuckets); } SLOG(this, log_level) << "Spent " << elapsed_time.InMillisecondsRoundedUp() << " milliseconds waiting for EBUSY."; } void ScanSession::AddSsid(const ByteString& ssid) { ssids_.insert(ssid); } // static bool ScanSession::CompareFrequencyCount( const WiFiProvider::FrequencyCount& first, const WiFiProvider::FrequencyCount& second) { return first.connection_count > second.connection_count; } } // namespace shill