shill: Fleshes-out ScanSession enough to initiate a wifi scan.

This CL adds the code required to initiate wifi scan.  This expects
wpa_supplicant to deal with the kernel's response.  The code does not,
yet, initiate a scan.  It just, now, contains what it needs to do so.

BUG=chromium:222088
TEST=unittest

Change-Id: Ibf8294bac455a61b3328a1db4079e0f939c9df6d
Reviewed-on: https://gerrit.chromium.org/gerrit/49272
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: David James <davidjames@chromium.org>
diff --git a/attribute_list.cc b/attribute_list.cc
index 33e1b95..849259d 100644
--- a/attribute_list.cc
+++ b/attribute_list.cc
@@ -29,10 +29,7 @@
 
 bool AttributeList::CreateAttribute(
     int id, AttributeList::NewFromIdMethod factory) {
-  if (ContainsKey(attributes_, id)) {
-    LOG(ERROR) << "Trying to re-add attribute: " << id;
-    return false;
-  }
+  LOG_IF(INFO, ContainsKey(attributes_, id)) << "Re-adding attribute: " << id;
   attributes_[id] = AttributePointer(factory.Run(id));
   return true;
 }
diff --git a/byte_string.cc b/byte_string.cc
index ea22637..f521e90 100644
--- a/byte_string.cc
+++ b/byte_string.cc
@@ -5,10 +5,14 @@
 #include "shill/byte_string.h"
 
 #include <netinet/in.h>
+#include <string.h>
+
+#include <algorithm>
 
 #include <base/string_number_conversions.h>
 
 using std::distance;
+using std::min;
 using std::string;
 using std::vector;
 
@@ -193,4 +197,14 @@
   }
 }
 
+// static
+bool ByteString::IsLessThan(const ByteString &lhs, const ByteString &rhs) {
+  size_t byte_count = min(lhs.GetLength(), rhs.GetLength());
+  int result = memcmp(lhs.GetConstData(), rhs.GetConstData(), byte_count);
+  if (result == 0) {
+    return byte_count == lhs.GetLength();
+  }
+  return result < 0;
+}
+
 }  // namespace shill
diff --git a/byte_string.h b/byte_string.h
index 1ba945a..73c83d2 100644
--- a/byte_string.h
+++ b/byte_string.h
@@ -108,6 +108,8 @@
   // not cause a copy).
   void RemovePrefix(size_t offset);
 
+  static bool IsLessThan(const ByteString &lhs, const ByteString &rhs);
+
  private:
   typedef std::vector<unsigned char> Vector;
 
diff --git a/netlink_manager.h b/netlink_manager.h
index a0072c0..a65ad6b 100644
--- a/netlink_manager.h
+++ b/netlink_manager.h
@@ -59,8 +59,6 @@
 #ifndef SHILL_NETLINK_MANAGER_H_
 #define SHILL_NETLINK_MANAGER_H_
 
-#include <gtest/gtest_prod.h>  // for FRIEND_TEST
-
 #include <list>
 #include <map>
 #include <set>
@@ -69,7 +67,10 @@
 #include <base/basictypes.h>
 #include <base/bind.h>
 #include <base/lazy_instance.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include "shill/io_handler.h"
 #include "shill/netlink_message.h"
 
 struct nlmsghdr;
@@ -79,7 +80,6 @@
 class Error;
 class EventDispatcher;
 struct InputData;
-class IOHandler;
 class NetlinkSocket;
 
 // NetlinkManager is a singleton that coordinates sending netlink messages to,
diff --git a/nl80211_message.cc b/nl80211_message.cc
index 9f53b69..738e0e4 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -618,6 +618,18 @@
 const uint8_t TriggerScanMessage::kCommand = NL80211_CMD_TRIGGER_SCAN;
 const char TriggerScanMessage::kCommandString[] = "NL80211_CMD_TRIGGER_SCAN";
 
+TriggerScanMessage::TriggerScanMessage()
+  : Nl80211Message(kCommand, kCommandString) {
+  attributes()->CreateAttribute(
+      NL80211_ATTR_IFINDEX, Bind(&NetlinkAttribute::NewNl80211AttributeFromId));
+  attributes()->CreateAttribute(
+      NL80211_ATTR_SCAN_FREQUENCIES,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId));
+  attributes()->CreateAttribute(
+      NL80211_ATTR_SCAN_SSIDS,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId));
+}
+
 const uint8_t UnprotDeauthenticateMessage::kCommand =
     NL80211_CMD_UNPROT_DEAUTHENTICATE;
 const char UnprotDeauthenticateMessage::kCommandString[] =
diff --git a/nl80211_message.h b/nl80211_message.h
index d9526a8..d6ed7af 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -411,7 +411,7 @@
   static const uint8_t kCommand;
   static const char kCommandString[];
 
-  TriggerScanMessage() : Nl80211Message(kCommand, kCommandString) {}
+  TriggerScanMessage();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TriggerScanMessage);
diff --git a/scan_session.cc b/scan_session.cc
index 737a432..67f2131 100644
--- a/scan_session.cc
+++ b/scan_session.cc
@@ -6,40 +6,76 @@
 
 #include <algorithm>
 #include <set>
+#include <string>
 #include <vector>
 
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
 #include <base/stl_util.h>
+#include <base/stringprintf.h>
 
+#include "shill/event_dispatcher.h"
 #include "shill/logging.h"
+#include "shill/netlink_manager.h"
+#include "shill/nl80211_attribute.h"
+#include "shill/nl80211_message.h"
 
+using base::Bind;
+using base::StringPrintf;
 using std::set;
+using std::string;
 using std::vector;
 
 namespace shill {
 
+const float ScanSession::kAllFrequencies = 1.1;
+const uint64_t ScanSession::kScanRetryDelayMilliseconds = 100;  // Arbitrary.
+const size_t ScanSession::kScanRetryCount = 10;
+
 ScanSession::ScanSession(
-    const WiFiProvider::FrequencyCountList &connected_frequency_list,
-    const vector<uint16_t> &unconnected_frequency_list)
-    : frequency_list_(connected_frequency_list),
+    NetlinkManager *netlink_manager,
+    EventDispatcher *dispatcher,
+    const WiFiProvider::FrequencyCountList &previous_frequencies,
+    const set<uint16_t> &available_frequencies,
+    uint32_t ifindex,
+    const FractionList &fractions,
+    int min_frequencies,
+    int max_frequencies,
+    OnScanFailed on_scan_failed)
+    : 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) {
-  SLOG(WiFi, 7) << "Frequency connections vector:";
+      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 + 1) {
   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<uint16_t> seen_frequencies;
-  for (const auto freq_conn : frequency_list_) {
-    SLOG(WiFi, 7) << "    freq[" << freq_conn.frequency << "] = "
-                  << freq_conn.connection_count;
+  for (const auto &freq_conn : frequency_list_) {
     seen_frequencies.insert(freq_conn.frequency);
     total_connections_ += freq_conn.connection_count;
   }
-
-  for (const auto freq : unconnected_frequency_list) {
+  for (const auto freq : available_frequencies) {
     if (!ContainsKey(seen_frequencies, freq)) {
       frequency_list_.push_back(WiFiProvider::FrequencyCount(freq, 0));
     }
   }
+
+  SLOG(WiFi, 7) << "Frequency connections vector:";
+  for (const auto &freq_conn : frequency_list_) {
+    SLOG(WiFi, 7) << "    freq[" << freq_conn.frequency << "] = "
+                  << freq_conn.connection_count;
+  }
 }
 
 ScanSession::~ScanSession() {}
@@ -51,7 +87,7 @@
 vector<uint16_t> ScanSession::GetScanFrequencies(float fraction_wanted,
                                                  size_t min_frequencies,
                                                  size_t max_frequencies) {
-  DCHECK(fraction_wanted >= 0);
+  DCHECK_GE(fraction_wanted, 0);
   total_fraction_wanted_ += fraction_wanted;
   float total_connects_wanted = total_fraction_wanted_ * total_connections_;
 
@@ -77,6 +113,109 @@
   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() {
+  DoScan(current_scan_frequencies_);
+}
+
+void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) {
+  TriggerScanMessage trigger_scan;
+  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(WiFi, 7) << "We have requested scan frequencies:";
+  string attribute_name;
+  int i = 0;
+  for (const auto freq : scan_frequencies) {
+    SLOG(WiFi, 7) << "  " << 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_->SendMessage(&trigger_scan,
+                                Bind(&ScanSession::OnTriggerScanResponse,
+                                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ScanSession::OnTriggerScanResponse(const NetlinkMessage &netlink_message) {
+  if (netlink_message.message_type() != NLMSG_ERROR) {
+    LOG(WARNING) << "Didn't expect _this_ message, here:";
+    netlink_message.Print(0, 0);
+    on_scan_failed_.Run();
+    return;
+  }
+
+  const ErrorAckMessage *error_ack_message =
+      dynamic_cast<const ErrorAckMessage *>(&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.";
+        on_scan_failed_.Run();
+        scan_tries_left_ = kScanRetryCount + 1;
+        return;
+      }
+      SLOG(WiFi, 3) << __func__ << " - trying again";
+      dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
+                                        weak_ptr_factory_.GetWeakPtr()),
+                                   kScanRetryDelayMilliseconds);
+      return;
+    }
+    on_scan_failed_.Run();
+  } else {
+    SLOG(WiFi, 6) << __func__ << ": Message ACKed";
+  }
+}
+
+void ScanSession::AddSsid(const ByteString &ssid) {
+  ssids_.insert(ssid);
+}
+
 // static
 bool ScanSession::CompareFrequencyCount(
     const WiFiProvider::FrequencyCount &first,
diff --git a/scan_session.h b/scan_session.h
index ce8d881..5d897af 100644
--- a/scan_session.h
+++ b/scan_session.h
@@ -5,30 +5,26 @@
 #ifndef SHILL_SCAN_SESSION_H_
 #define SHILL_SCAN_SESSION_H_
 
+#include <deque>
+#include <set>
 #include <vector>
 
 #include <base/basictypes.h>
+#include <base/callback.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include "shill/byte_string.h"
 #include "shill/wifi_provider.h"
 
 namespace shill {
 
-// Contains the state of a progressive wifi scan (for example, a list of the
-// requested frequencies and an indication of which of those still need to be
-// scanned).  A wifi scan using ScanSession can transpire across multiple
-// requests, each one encompassing a different set of frequencies.
-//
-// Use this as follows (note, this is shown as synchronous code for clarity
-// but it really should be implemented as asynchronous code):
-//
-// ScanSession scan_session(frequencies_seen_ever, all_scan_frequencies_);
-// while (scan_session.HasMoreFrequencies()) {
-//   scan_session.InitiateScan(scan_session.GetScanFrequencies(
-//      kScanFraction, kMinScanFrequencies, kMaxScanFrequencies));
-//  // Wait for scan results.
-// }
-//
-// The sequence for a single scan is:
+class EventDispatcher;
+class NetlinkManager;
+class NetlinkMessage;
+
+// |ScanSession| sends requests to the kernel to scan WiFi frequencies for
+// access points.  The sequence for a single scan is as follows:
 //
 //   +-------------+                                                +--------+
 //   | ScanSession |                                                | Kernel |
@@ -43,35 +39,103 @@
 //       |<-- NL80211_CMD_NEW_SCAN_RESULTS (reply, unicast, NLM_F_MULTI) -|
 //       |                                                                |
 //
-// ScanSession::OnNewScanBroadcast handles the broadcast
-// NL80211_CMD_NEW_SCAN_RESULTS by issuing a NL80211_CMD_GET_SCAN and
-// installing OnNewScanUnicast to handle the unicast
-// NL80211_CMD_NEW_SCAN_RESULTS.
+// Scanning WiFi frequencies for access points takes a long time (on the order
+// of 100ms per frequency and the kernel doesn't return the result until the
+// answers are ready for all the frequencies in the batch).  Given this,
+// scanning all frequencies in one batch takes a very long time.
+//
+// A ScanSession is used to distribute a scan across multiple requests (hoping
+// that a successful connection will result from an early request thereby
+// obviating the need for the remainder of the scan).  A ScanSession can be
+// used as follows (note, this is shown as synchronous code for clarity
+// but it really should be implemented as asynchronous code):
+//
+// ScanSession::FractionList scan_fractions;
+// scan_fractions.push_back(<some value>);
+// ...
+// scan_fractions.push_back(<some value>);
+// ScanSession scan_session(netlink_manager_, dispatcher(),
+//                          frequencies_seen_ever, all_scan_frequencies_,
+//                          interface_index(), scan_fractions,
+//                          kMinScanFrequencies, kMaxScanFrequencies,
+//                          on_scan_failed);
+// while (scan_session.HasMoreFrequencies()) {
+//   scan_session.InitiateScan();
+//   // Wait for scan results.  In the current WiFi code, this means wait
+//   // until |WiFi::ScanDone| is called.
+// }
 
 class ScanSession {
  public:
-  // The frequency lists provide the frequencies that are returned by
-  // |GetScanFrequencies|.  Frequencies are taken, first, from the connected
-  // list (in order of the number of connections per frequency -- high before
-  // low) and then from the unconnected list (in the order provided).
-  ScanSession(const WiFiProvider::FrequencyCountList &connected_frequency_list,
-              const std::vector<uint16_t> &unconnected_frequency_list);
+  typedef base::Closure OnScanFailed;
+  typedef std::deque<float> FractionList;
+  // Used as a fraction in |FractionList| to indicate that future scans in
+  // this session should not be limited to a subset of the frequencies we've
+  // already seen.
+  static const float kAllFrequencies;
+
+  // Sets up a new progressive scan session.  Uses |netlink_manager| to send
+  // NL80211_CMD_TRIGGER_SCAN messages to the kernel (uses |dispatcher| to
+  // reissue those commands if a send request returns EBUSY).  Multiple scans
+  // for APs on wifi device |ifindex| are issued (one for each call to
+  // |InitiateScan|) on wifi frequencies taken from the union of unique
+  // frequencies in |previous_frequencies| and |available_frequencies| (most
+  // commonly seen frequencies before less commonly seen ones followed by
+  // never-before seen frequencies, the latter in an unspecified order).
+  //
+  // Each scan takes a greater percentile (described by the values in
+  // |fractions|) of the previously seen frequencies (but no less than
+  // |min_frequencies| and no more than |max_frequencies|).  After all
+  // previously seen frequencies have been requested, each |InitiateScan|
+  // scans the next |max_frequencies| until all |available_frequencies| have
+  // been exhausted.
+  //
+  // If a scan request to the kernel returns an error, |on_scan_failed| is
+  // called.  The caller can reissue the scan by calling |ReInitiateScan| or
+  // abort the scan session by deleting the |ScanSession| object.
+  ScanSession(NetlinkManager *netlink_manager,
+              EventDispatcher *dispatcher,
+              const WiFiProvider::FrequencyCountList &previous_frequencies,
+              const std::set<uint16_t> &available_frequencies,
+              uint32_t ifindex,
+              const FractionList &fractions,
+              int min_frequencies,
+              int max_frequencies,
+              OnScanFailed on_scan_failed);
 
   virtual ~ScanSession();
 
   // Returns true if |ScanSession| contains unscanned frequencies.
   bool HasMoreFrequencies() const;
 
-  // Scanning WiFi frequencies for access points takes a long time (on the
-  // order of 100ms per frequency and the kernel doesn't return the result until
-  // the answers are ready for all the frequencies in the batch).  Given this,
-  // scanning all frequencies in one batch takes a very long time.
-  // |GetScanFrequencies| is intended to be called multiple times in order to
-  // get a number of small batches of frequencies to scan.  Frequencies most
-  // likely to yield a successful connection (based on previous connections)
-  // are returned first followed by less-likely frequencies followed, finally,
-  // by frequencies to which this machine hasn't connected before.
-  //
+  // Adds an SSID to the list of things for which to scan.  Useful for hidden
+  // SSIDs.
+  void AddSsid(const ByteString &ssid);
+
+  // Start a wifi scan of the next set of frequencies (derived from the
+  // constructor's parameters) after saving those frequencies for the potential
+  // need to reinitiate a scan.
+  virtual void InitiateScan();
+
+  // Re-issues the previous scan (i.e., it uses the same frequency list as the
+  // previous scan).  Other classes may use this when |on_scan_failed| is
+  // called.  Called by |OnTriggerScanResponse| when the previous attempt to do
+  // a scan fails.
+  void ReInitiateScan();
+
+ private:
+  friend class ScanSessionTest;
+  // Milliseconds to wait before retrying a failed scan.
+  static const uint64_t kScanRetryDelayMilliseconds;
+  // Number of times to retry a failed scan before giving up and calling
+  // |on_scan_failed_|.
+  static const size_t kScanRetryCount;
+
+  // Assists with sorting the |previous_frequencies| passed to the
+  // constructor.
+  static bool CompareFrequencyCount(const WiFiProvider::FrequencyCount &first,
+                                    const WiFiProvider::FrequencyCount &second);
+
   // |GetScanFrequencies| gets the next set of WiFi scan frequencies.  Returns
   // at least |min_frequencies| (unless fewer frequencies remain from previous
   // calls) and no more than |max_frequencies|.  Inside these constraints,
@@ -97,13 +161,20 @@
                                                    size_t min_frequencies,
                                                    size_t max_frequencies);
 
- private:
-  friend class ScanSessionTest;
+  // Does the real work of initiating a scan by sending an
+  // NL80211_CMD_TRIGGER_SCAN message to the kernel and installing a handler for
+  // any response (which only happens in the error case).
+  void DoScan(const std::vector<uint16_t> &scan_frequencies);
 
-  // Assists with sorting the |connected_frequency_list| passed to the
-  // constructor.
-  static bool CompareFrequencyCount(const WiFiProvider::FrequencyCount &first,
-                                    const WiFiProvider::FrequencyCount &second);
+  // Handles any unicast response to NL80211_CMD_TRIGGER_SCAN (which is,
+  // likely, an error -- when things work, we get an
+  // NL80211_CMD_NEW_SCAN_RESULTS broadcast message).
+  void OnTriggerScanResponse(const NetlinkMessage &message);
+
+  base::WeakPtrFactory<ScanSession> weak_ptr_factory_;
+
+  NetlinkManager *netlink_manager_;
+  EventDispatcher *dispatcher_;
 
   // List of frequencies, sorted by the number of successful connections for
   // each frequency.
@@ -111,6 +182,14 @@
   size_t total_connections_;
   size_t total_connects_provided_;
   float total_fraction_wanted_;
+  std::vector<uint16_t> current_scan_frequencies_;
+  uint32_t wifi_interface_index_;
+  std::set<ByteString, bool(*)(const ByteString &, const ByteString &)> ssids_;
+  FractionList fractions_;
+  size_t min_frequencies_;
+  size_t max_frequencies_;
+  OnScanFailed on_scan_failed_;
+  size_t scan_tries_left_;
 
   DISALLOW_COPY_AND_ASSIGN(ScanSession);
 };
diff --git a/scan_session_unittest.cc b/scan_session_unittest.cc
index e789cde..4022416 100644
--- a/scan_session_unittest.cc
+++ b/scan_session_unittest.cc
@@ -8,11 +8,9 @@
 #include <set>
 #include <vector>
 
-#include <base/memory/scoped_ptr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "shill/manager.h"
 #include "shill/mock_netlink_manager.h"
 #include "shill/netlink_manager.h"
 
@@ -20,8 +18,6 @@
 using std::vector;
 using testing::_;
 using testing::ContainerEq;
-using testing::Invoke;
-using testing::Return;
 using testing::Test;
 
 namespace shill {
@@ -54,40 +50,47 @@
   kExpectedFreq2412
 };
 
-// A number larger than 1 to make sure that ScanSession doesn't just snag up
-// to 100 percent and stop.
-static float kEverything = 1.1;
-
 class ScanSessionTest : public Test {
  public:
   // Test set of "all the other frequencies this device can support" in
   // sorted order.
-  ScanSession *ConfigureScanSession() {
-    WiFiProvider::FrequencyCountList connected_frequencies =
-        GetScanFrequencies();
-
-    vector<uint16_t> unconnected_frequencies(
-        kUnconnectedFrequencies,
-        kUnconnectedFrequencies + arraysize(kUnconnectedFrequencies));
-    ScanSession *scan_session(new ScanSession(connected_frequencies,
-                                              unconnected_frequencies));
-    return scan_session;
-  }
-
-  WiFiProvider::FrequencyCountList GetScanFrequencies() const {
-    WiFiProvider::FrequencyCountList freq_connects_list(
+  ScanSessionTest() {
+    WiFiProvider::FrequencyCountList connected_frequencies(
         kConnectedFrequencies,
         kConnectedFrequencies + arraysize(kConnectedFrequencies));
-    return freq_connects_list;
+
+    set<uint16_t> unconnected_frequencies(
+        kUnconnectedFrequencies,
+        kUnconnectedFrequencies + arraysize(kUnconnectedFrequencies));
+    const int kArbitraryMinimum = 1;
+    const int kArbitraryMaximum = std::numeric_limits<int>::max();
+    ScanSession::OnScanFailed null_error_handler;
+    scan_session_.reset(new ScanSession(&netlink_manager_,
+                                        NULL,
+                                        connected_frequencies,
+                                        unconnected_frequencies,
+                                        0,
+                                        ScanSession::FractionList(),
+                                        kArbitraryMinimum,
+                                        kArbitraryMaximum,
+                                        null_error_handler));
   }
 
+  virtual std::vector<uint16_t> GetScanFrequencies(float scan_fraction,
+                                                   size_t min_frequencies,
+                                                   size_t max_frequencies) {
+    return scan_session_->GetScanFrequencies(scan_fraction, min_frequencies,
+                                             max_frequencies);
+  }
+  ScanSession *scan_session() { return scan_session_.get(); }
+
  private:
+  scoped_ptr<ScanSession> scan_session_;
   MockNetlinkManager netlink_manager_;
 };
 
 // Test that we can get a bunch of frequencies up to a specified fraction.
 TEST_F(ScanSessionTest, FractionTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 83% of the connected values.
@@ -96,10 +99,9 @@
     expected.push_back(kExpectedFreq5640);
     expected.push_back(kExpectedFreq5600);
     expected.push_back(kExpectedFreq5580);
-    result = scan_session->GetScanFrequencies(
-        .83, 1, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(.83, 1, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // Get the next 4 values.
@@ -107,30 +109,29 @@
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5560);
     expected.push_back(kExpectedFreq5620);
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    result = scan_session->GetScanFrequencies(kEverything, 1, 4);
+    expected.push_back(kExpectedFreq2412);
+    expected.push_back(kExpectedFreq2417);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 1, 4);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the remaining list.
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
-    expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(
-        kEverything, 20, std::numeric_limits<size_t>::max());
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 20,
+                                std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get a bunch of frequencies up to a specified fraction,
 // followed by another group up to a specified fraction.
 TEST_F(ScanSessionTest, TwoFractionsTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 60% of the connected values.
@@ -138,10 +139,9 @@
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5640);
     expected.push_back(kExpectedFreq5600);
-    result = scan_session->GetScanFrequencies(
-        .60, 0, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(.60, 0, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // Get the next 32% of the connected values.
@@ -149,33 +149,31 @@
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5580);
     expected.push_back(kExpectedFreq5560);
-    result = scan_session->GetScanFrequencies(
-        .32, 0, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(.32, 0, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the remaining list.
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5620);
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
     expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(
-        kEverything, std::numeric_limits<size_t>::max(),
-        std::numeric_limits<size_t>::max());
+    expected.push_back(kExpectedFreq2417);
+    expected.push_back(kExpectedFreq2422);
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies,
+                                std::numeric_limits<size_t>::max(),
+                                std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get a bunch of frequencies up to a minimum count, even
 // when the requested fraction has already been reached.
 TEST_F(ScanSessionTest, MinTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 3 previously seen values.
@@ -184,42 +182,39 @@
     expected.push_back(kExpectedFreq5640);
     expected.push_back(kExpectedFreq5600);
     expected.push_back(kExpectedFreq5580);
-    result = scan_session->GetScanFrequencies(
-        .30, 3, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(.30, 3, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // Get the next value by requensting a minimum of 1.
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5560);
-    result = scan_session->GetScanFrequencies(
-        0.0, 1, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(0.0, 1, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the remaining list.
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5620);
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
     expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(
-        kEverything, std::numeric_limits<size_t>::max(),
-        std::numeric_limits<size_t>::max());
+    expected.push_back(kExpectedFreq2417);
+    expected.push_back(kExpectedFreq2422);
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies,
+                                std::numeric_limits<size_t>::max(),
+                                std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get up to a specified maximum number of frequencies.
 TEST_F(ScanSessionTest, MaxTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 7 values (crosses seen/unseen boundary).
@@ -230,30 +225,29 @@
     expected.push_back(kExpectedFreq5580);
     expected.push_back(kExpectedFreq5560);
     expected.push_back(kExpectedFreq5620);
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    result = scan_session->GetScanFrequencies(kEverything, 1, 7);
+    expected.push_back(kExpectedFreq2412);
+    expected.push_back(kExpectedFreq2417);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 1, 7);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the remaining list.
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
-    expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(
-        kEverything, 20, std::numeric_limits<size_t>::max());
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 20,
+                                std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get exactly the seen frequencies and exactly the unseen
 // ones.
 TEST_F(ScanSessionTest, ExactTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 5 values -- exectly on the seen/unseen border.
@@ -264,28 +258,27 @@
     expected.push_back(kExpectedFreq5580);
     expected.push_back(kExpectedFreq5560);
     expected.push_back(kExpectedFreq5620);
-    result = scan_session->GetScanFrequencies(kEverything, 5, 5);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 5, 5);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the last 5.
   {
     vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
     expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(kEverything, 5, 5);
+    expected.push_back(kExpectedFreq2417);
+    expected.push_back(kExpectedFreq2422);
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies, 5, 5);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get everything in one read.
 TEST_F(ScanSessionTest, AllOneReadTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
 
   vector<uint16_t> expected;
   expected.push_back(kExpectedFreq5640);
@@ -293,23 +286,22 @@
   expected.push_back(kExpectedFreq5580);
   expected.push_back(kExpectedFreq5560);
   expected.push_back(kExpectedFreq5620);
-  expected.push_back(kExpectedFreq2432);
-  expected.push_back(kExpectedFreq2427);
-  expected.push_back(kExpectedFreq2422);
-  expected.push_back(kExpectedFreq2417);
   expected.push_back(kExpectedFreq2412);
+  expected.push_back(kExpectedFreq2417);
+  expected.push_back(kExpectedFreq2422);
+  expected.push_back(kExpectedFreq2427);
+  expected.push_back(kExpectedFreq2432);
   vector<uint16_t> result;
-  result = scan_session->GetScanFrequencies(
-      kEverything, std::numeric_limits<size_t>::max(),
-      std::numeric_limits<size_t>::max());
+  result = GetScanFrequencies(ScanSession::kAllFrequencies,
+                              std::numeric_limits<size_t>::max(),
+                              std::numeric_limits<size_t>::max());
   EXPECT_THAT(result, ContainerEq(expected));
-  EXPECT_FALSE(scan_session->HasMoreFrequencies());
+  EXPECT_FALSE(scan_session()->HasMoreFrequencies());
 }
 
 // Test that we can get all the previously seen frequencies (and only the
 // previously seen frequencies) via the requested fraction.
 TEST_F(ScanSessionTest, EverythingFractionTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
 
   // Get the first 100% of the connected values.
@@ -320,103 +312,101 @@
     expected.push_back(kExpectedFreq5580);
     expected.push_back(kExpectedFreq5560);
     expected.push_back(kExpectedFreq5620);
-    result = scan_session->GetScanFrequencies(
-        1.0, 0, std::numeric_limits<size_t>::max());
+    result = GetScanFrequencies(1.0, 0, std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
 
   // And, get the remaining list.
   {
     vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2432);
-    expected.push_back(kExpectedFreq2427);
-    expected.push_back(kExpectedFreq2422);
-    expected.push_back(kExpectedFreq2417);
     expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(
-        kEverything, std::numeric_limits<size_t>::max(),
-        std::numeric_limits<size_t>::max());
+    expected.push_back(kExpectedFreq2417);
+    expected.push_back(kExpectedFreq2422);
+    expected.push_back(kExpectedFreq2427);
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(ScanSession::kAllFrequencies,
+                                std::numeric_limits<size_t>::max(),
+                                std::numeric_limits<size_t>::max());
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }
 
 // Test that we can get each value individually.
 TEST_F(ScanSessionTest, IndividualReadsTest) {
-  scoped_ptr<ScanSession> scan_session(ConfigureScanSession());
   vector<uint16_t> result;
   static const float kArbitraryFraction = 0.83;
 
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5640);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5600);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5580);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5560);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq5620);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
-  }
-  {
-    vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2432);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
-    EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
-  }
-  {
-    vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2427);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
-    EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
-  }
-  {
-    vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2422);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
-    EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
-  }
-  {
-    vector<uint16_t> expected;
-    expected.push_back(kExpectedFreq2417);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
-    EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_TRUE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
   }
   {
     vector<uint16_t> expected;
     expected.push_back(kExpectedFreq2412);
-    result = scan_session->GetScanFrequencies(kArbitraryFraction, 1, 1);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
     EXPECT_THAT(result, ContainerEq(expected));
-    EXPECT_FALSE(scan_session->HasMoreFrequencies());
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
+  }
+  {
+    vector<uint16_t> expected;
+    expected.push_back(kExpectedFreq2417);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
+    EXPECT_THAT(result, ContainerEq(expected));
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
+  }
+  {
+    vector<uint16_t> expected;
+    expected.push_back(kExpectedFreq2422);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
+    EXPECT_THAT(result, ContainerEq(expected));
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
+  }
+  {
+    vector<uint16_t> expected;
+    expected.push_back(kExpectedFreq2427);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
+    EXPECT_THAT(result, ContainerEq(expected));
+    EXPECT_TRUE(scan_session()->HasMoreFrequencies());
+  }
+  {
+    vector<uint16_t> expected;
+    expected.push_back(kExpectedFreq2432);
+    result = GetScanFrequencies(kArbitraryFraction, 1, 1);
+    EXPECT_THAT(result, ContainerEq(expected));
+    EXPECT_FALSE(scan_session()->HasMoreFrequencies());
   }
 }