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 | #ifndef SHILL_SCAN_SESSION_H_ |
| 6 | #define SHILL_SCAN_SESSION_H_ |
| 7 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 8 | #include <deque> |
| 9 | #include <set> |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 10 | #include <vector> |
| 11 | |
| 12 | #include <base/basictypes.h> |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 13 | #include <base/callback.h> |
| 14 | #include <base/memory/weak_ptr.h> |
| 15 | #include <gtest/gtest_prod.h> // for FRIEND_TEST |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 16 | #include <metrics/timer.h> |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 17 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 18 | #include "shill/byte_string.h" |
Wade Guthrie | 84db7ce | 2013-06-12 11:40:49 -0700 | [diff] [blame] | 19 | #include "shill/netlink_manager.h" |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 20 | #include "shill/wifi_provider.h" |
| 21 | |
| 22 | namespace shill { |
| 23 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 24 | class EventDispatcher; |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 25 | class Metrics; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 26 | class NetlinkManager; |
| 27 | class NetlinkMessage; |
Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 28 | class Nl80211Message; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 29 | |
| 30 | // |ScanSession| sends requests to the kernel to scan WiFi frequencies for |
| 31 | // access points. The sequence for a single scan is as follows: |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 32 | // |
| 33 | // +-------------+ +--------+ |
| 34 | // | ScanSession | | Kernel | |
| 35 | // +---+---------+ +-----+--+ |
| 36 | // |--- NL80211_CMD_TRIGGER_SCAN ---------------------------------->| |
| 37 | // |<-- NL80211_CMD_TRIGGER_SCAN (broadcast) -----------------------| |
| 38 | // |<-- NL80211_CMD_NEW_SCAN_RESULTS (broadcast) -------------------| |
| 39 | // |--- NL80211_CMD_GET_SCAN -------------------------------------->| |
| 40 | // |<-- NL80211_CMD_NEW_SCAN_RESULTS (reply, unicast, NLM_F_MULTI) -| |
| 41 | // |<-- NL80211_CMD_NEW_SCAN_RESULTS (reply, unicast, NLM_F_MULTI) -| |
| 42 | // | ... | |
| 43 | // |<-- NL80211_CMD_NEW_SCAN_RESULTS (reply, unicast, NLM_F_MULTI) -| |
| 44 | // | | |
| 45 | // |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 46 | // Scanning WiFi frequencies for access points takes a long time (on the order |
| 47 | // of 100ms per frequency and the kernel doesn't return the result until the |
| 48 | // answers are ready for all the frequencies in the batch). Given this, |
| 49 | // scanning all frequencies in one batch takes a very long time. |
| 50 | // |
| 51 | // A ScanSession is used to distribute a scan across multiple requests (hoping |
| 52 | // that a successful connection will result from an early request thereby |
| 53 | // obviating the need for the remainder of the scan). A ScanSession can be |
| 54 | // used as follows (note, this is shown as synchronous code for clarity |
| 55 | // but it really should be implemented as asynchronous code): |
| 56 | // |
| 57 | // ScanSession::FractionList scan_fractions; |
| 58 | // scan_fractions.push_back(<some value>); |
| 59 | // ... |
| 60 | // scan_fractions.push_back(<some value>); |
| 61 | // ScanSession scan_session(netlink_manager_, dispatcher(), |
| 62 | // frequencies_seen_ever, all_scan_frequencies_, |
| 63 | // interface_index(), scan_fractions, |
| 64 | // kMinScanFrequencies, kMaxScanFrequencies, |
| 65 | // on_scan_failed); |
| 66 | // while (scan_session.HasMoreFrequencies()) { |
| 67 | // scan_session.InitiateScan(); |
| 68 | // // Wait for scan results. In the current WiFi code, this means wait |
| 69 | // // until |WiFi::ScanDone| is called. |
| 70 | // } |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 71 | |
| 72 | class ScanSession { |
| 73 | public: |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 74 | typedef base::Closure OnScanFailed; |
| 75 | typedef std::deque<float> FractionList; |
| 76 | // Used as a fraction in |FractionList| to indicate that future scans in |
| 77 | // this session should not be limited to a subset of the frequencies we've |
| 78 | // already seen. |
| 79 | static const float kAllFrequencies; |
| 80 | |
| 81 | // Sets up a new progressive scan session. Uses |netlink_manager| to send |
| 82 | // NL80211_CMD_TRIGGER_SCAN messages to the kernel (uses |dispatcher| to |
| 83 | // reissue those commands if a send request returns EBUSY). Multiple scans |
| 84 | // for APs on wifi device |ifindex| are issued (one for each call to |
| 85 | // |InitiateScan|) on wifi frequencies taken from the union of unique |
| 86 | // frequencies in |previous_frequencies| and |available_frequencies| (most |
| 87 | // commonly seen frequencies before less commonly seen ones followed by |
| 88 | // never-before seen frequencies, the latter in an unspecified order). |
| 89 | // |
| 90 | // Each scan takes a greater percentile (described by the values in |
| 91 | // |fractions|) of the previously seen frequencies (but no less than |
| 92 | // |min_frequencies| and no more than |max_frequencies|). After all |
| 93 | // previously seen frequencies have been requested, each |InitiateScan| |
| 94 | // scans the next |max_frequencies| until all |available_frequencies| have |
| 95 | // been exhausted. |
| 96 | // |
| 97 | // If a scan request to the kernel returns an error, |on_scan_failed| is |
| 98 | // called. The caller can reissue the scan by calling |ReInitiateScan| or |
| 99 | // abort the scan session by deleting the |ScanSession| object. |
| 100 | ScanSession(NetlinkManager *netlink_manager, |
| 101 | EventDispatcher *dispatcher, |
| 102 | const WiFiProvider::FrequencyCountList &previous_frequencies, |
| 103 | const std::set<uint16_t> &available_frequencies, |
| 104 | uint32_t ifindex, |
| 105 | const FractionList &fractions, |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 106 | size_t min_frequencies, |
| 107 | size_t max_frequencies, |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 108 | OnScanFailed on_scan_failed, |
| 109 | Metrics *metrics); |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 110 | |
| 111 | virtual ~ScanSession(); |
| 112 | |
| 113 | // Returns true if |ScanSession| contains unscanned frequencies. |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 114 | virtual bool HasMoreFrequencies() const; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 115 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 116 | // Adds an SSID to the list of things for which to scan. Useful for hidden |
| 117 | // SSIDs. |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 118 | virtual void AddSsid(const ByteString &ssid); |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 119 | |
| 120 | // Start a wifi scan of the next set of frequencies (derived from the |
| 121 | // constructor's parameters) after saving those frequencies for the potential |
| 122 | // need to reinitiate a scan. |
| 123 | virtual void InitiateScan(); |
| 124 | |
| 125 | // Re-issues the previous scan (i.e., it uses the same frequency list as the |
| 126 | // previous scan). Other classes may use this when |on_scan_failed| is |
| 127 | // called. Called by |OnTriggerScanResponse| when the previous attempt to do |
| 128 | // a scan fails. |
| 129 | void ReInitiateScan(); |
| 130 | |
| 131 | private: |
| 132 | friend class ScanSessionTest; |
Wade Guthrie | 5a4e2ef | 2013-04-30 12:51:39 -0700 | [diff] [blame] | 133 | friend class WiFiObjectTest; // OnTriggerScanResponse. |
| 134 | FRIEND_TEST(ScanSessionTest, EBusy); |
| 135 | FRIEND_TEST(ScanSessionTest, OnError); |
| 136 | FRIEND_TEST(ScanSessionTest, OnTriggerScanResponse); |
| 137 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 138 | // Milliseconds to wait before retrying a failed scan. |
| 139 | static const uint64_t kScanRetryDelayMilliseconds; |
| 140 | // Number of times to retry a failed scan before giving up and calling |
| 141 | // |on_scan_failed_|. |
| 142 | static const size_t kScanRetryCount; |
| 143 | |
| 144 | // Assists with sorting the |previous_frequencies| passed to the |
| 145 | // constructor. |
| 146 | static bool CompareFrequencyCount(const WiFiProvider::FrequencyCount &first, |
| 147 | const WiFiProvider::FrequencyCount &second); |
| 148 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 149 | // |GetScanFrequencies| gets the next set of WiFi scan frequencies. Returns |
| 150 | // at least |min_frequencies| (unless fewer frequencies remain from previous |
| 151 | // calls) and no more than |max_frequencies|. Inside these constraints, |
| 152 | // |GetScanFrequencies| tries to return at least the number of frequencies |
| 153 | // required to reach the connection fraction |scan_fraction| out of the total |
| 154 | // number of previous connections. For example, the first call requesting |
| 155 | // 33.3% will return the minimum number frequencies that add up to _at least_ |
| 156 | // the 33.3rd percentile of frequencies to which we've successfully connected |
| 157 | // in the past. The next call of 33.3% returns the minimum number of |
| 158 | // frequencies required so that the total of the frequencies returned are _at |
| 159 | // least_ the 66.6th percentile of the frequencies to which we've successfully |
| 160 | // connected. |
| 161 | // |
| 162 | // For example, say we've connected to 3 frequencies before: |
| 163 | // freq a,count=10; freq b,count=5; freq c,count=5. |
| 164 | // |
| 165 | // GetScanFrequencies(.50,2,10) // Returns a & b (|a| reaches %ile but |b| is |
| 166 | // // required to meet the minimum). |
| 167 | // GetScanFrequencies(.51,2,10) // Returns c & 9 frequencies from the list |
| 168 | // // of frequencies to which we've never |
| 169 | // // connected. |
| 170 | virtual std::vector<uint16_t> GetScanFrequencies(float scan_fraction, |
| 171 | size_t min_frequencies, |
| 172 | size_t max_frequencies); |
| 173 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 174 | // Does the real work of initiating a scan by sending an |
| 175 | // NL80211_CMD_TRIGGER_SCAN message to the kernel and installing a handler for |
| 176 | // any response (which only happens in the error case). |
| 177 | void DoScan(const std::vector<uint16_t> &scan_frequencies); |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 178 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 179 | // Handles any unicast response to NL80211_CMD_TRIGGER_SCAN (which is, |
| 180 | // likely, an error -- when things work, we get an |
| 181 | // NL80211_CMD_NEW_SCAN_RESULTS broadcast message). |
Wade Guthrie | 7347bf2 | 2013-04-30 11:21:51 -0700 | [diff] [blame] | 182 | void OnTriggerScanResponse(const Nl80211Message &message); |
Wade Guthrie | 84db7ce | 2013-06-12 11:40:49 -0700 | [diff] [blame] | 183 | void OnTriggerScanErrorResponse(NetlinkManager::AuxilliaryMessageType type, |
| 184 | const NetlinkMessage *netlink_message); |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 185 | void ReportEbusyTime(int log_level); |
| 186 | |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 187 | // Logs the results of the scan. |
| 188 | void ReportResults(int log_level); |
| 189 | |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 190 | base::WeakPtrFactory<ScanSession> weak_ptr_factory_; |
| 191 | |
| 192 | NetlinkManager *netlink_manager_; |
| 193 | EventDispatcher *dispatcher_; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 194 | |
| 195 | // List of frequencies, sorted by the number of successful connections for |
| 196 | // each frequency. |
| 197 | WiFiProvider::FrequencyCountList frequency_list_; |
| 198 | size_t total_connections_; |
| 199 | size_t total_connects_provided_; |
| 200 | float total_fraction_wanted_; |
Wade Guthrie | b9c3feb | 2013-04-25 16:31:19 -0700 | [diff] [blame] | 201 | std::vector<uint16_t> current_scan_frequencies_; |
| 202 | uint32_t wifi_interface_index_; |
| 203 | std::set<ByteString, bool(*)(const ByteString &, const ByteString &)> ssids_; |
| 204 | FractionList fractions_; |
| 205 | size_t min_frequencies_; |
| 206 | size_t max_frequencies_; |
| 207 | OnScanFailed on_scan_failed_; |
| 208 | size_t scan_tries_left_; |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 209 | bool found_error_; |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 210 | |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 211 | // Statistics gathering. |
Wade Guthrie | 7b310a1 | 2013-05-28 15:37:13 -0700 | [diff] [blame] | 212 | size_t original_frequency_count_; |
Wade Guthrie | f22681f | 2013-05-31 11:46:31 -0700 | [diff] [blame] | 213 | chromeos_metrics::Timer ebusy_timer_; |
| 214 | Metrics *metrics_; |
| 215 | |
Wade Guthrie | a60a11c | 2013-04-12 17:47:34 -0700 | [diff] [blame] | 216 | DISALLOW_COPY_AND_ASSIGN(ScanSession); |
| 217 | }; |
| 218 | |
| 219 | } // namespace shill. |
| 220 | |
| 221 | #endif // SHILL_SCAN_SESSION_H_ |