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