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