shill: Installs progressive scan in shill.
BUG=chromium:222088
TEST=unittest and manual. The manual tests consist of:
- Enable progressive scan. On target, in a crosh window, do:
o progressive_scan on
- Start shill with log=-10/wifi. On the target, in a shell window,
do the following:
o stop shill
o shill --log-level=-10 --log-scopes=wifi
- Wait five seconds (the code will do everything it needs to at
startup).
- Look in /var/log/net.log and verify the existence of the following:
o Doing progressive scan
o ProgressiveScanTask - scan requested for XXXX
Initiating a scan -- returning
o ProgressiveScanTask - scan requested for XXXX
Ignoring scan request wile connecting to an AP
Change-Id: Ie577869aedc03230e1fab62fc8cb9f0b3850c7fb
Reviewed-on: https://gerrit.chromium.org/gerrit/50377
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/Makefile b/Makefile
index 58507e9..5fc8edd 100644
--- a/Makefile
+++ b/Makefile
@@ -422,6 +422,7 @@
mock_metrics.o \
mock_minijail.o \
mock_modem_info.o \
+ mock_netlink_manager.o \
mock_nss.o \
mock_pending_activation_store.o \
mock_portal_detector.o \
@@ -434,6 +435,7 @@
mock_resolver.o \
mock_routing_table.o \
mock_rtnl_handler.o \
+ mock_scan_session.o \
mock_service.o \
mock_socket_info_reader.o \
mock_sockets.o \
diff --git a/attribute_list.cc b/attribute_list.cc
index 849259d..a0843ea 100644
--- a/attribute_list.cc
+++ b/attribute_list.cc
@@ -29,7 +29,10 @@
bool AttributeList::CreateAttribute(
int id, AttributeList::NewFromIdMethod factory) {
- LOG_IF(INFO, ContainsKey(attributes_, id)) << "Re-adding attribute: " << id;
+ if (ContainsKey(attributes_, id)) {
+ LOG(WARNING) << "Trying to re-add attribute " << id << ", not overwriting";
+ return true;
+ }
attributes_[id] = AttributePointer(factory.Run(id));
return true;
}
diff --git a/metrics.h b/metrics.h
index c4971f2..0e2656d 100644
--- a/metrics.h
+++ b/metrics.h
@@ -500,7 +500,7 @@
void NotifyDeviceScanStarted(int interface_index);
// Notifies this object that a device has completed the scanning process.
- void NotifyDeviceScanFinished(int interface_index);
+ virtual void NotifyDeviceScanFinished(int interface_index);
// Notifies this object that a device has started the connect process.
void NotifyDeviceConnectStarted(int interface_index,
diff --git a/mock_metrics.h b/mock_metrics.h
index 00f78c1..40ac679 100644
--- a/mock_metrics.h
+++ b/mock_metrics.h
@@ -5,19 +5,20 @@
#ifndef SHILL_MOCK_METRICS_
#define SHILL_MOCK_METRICS_
-#include <gmock/gmock.h>
-
#include "shill/metrics.h"
+#include <gmock/gmock.h>
+
namespace shill {
class MockMetrics : public Metrics {
public:
- MockMetrics(EventDispatcher *dispatcher);
+ explicit MockMetrics(EventDispatcher *dispatcher);
virtual ~MockMetrics();
MOCK_METHOD0(Start, void());
MOCK_METHOD0(Stop, void());
+ MOCK_METHOD1(NotifyDeviceScanFinished, void(int interface_index));
MOCK_METHOD1(NotifyDefaultServiceChanged, void(const Service *service));
MOCK_METHOD2(NotifyServiceStateChanged,
void(const Service *service, Service::ConnectState new_state));
@@ -31,6 +32,6 @@
DISALLOW_COPY_AND_ASSIGN(MockMetrics);
};
-}
+} // namespace shill
#endif // SHILL_MOCK_METRICS_
diff --git a/mock_netlink_manager.cc b/mock_netlink_manager.cc
new file mode 100644
index 0000000..55234be
--- /dev/null
+++ b/mock_netlink_manager.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/mock_netlink_manager.h"
+
+#include <string>
+
+#include <base/stl_util.h>
+
+#include "shill/netlink_message.h"
+
+using std::string;
+
+namespace shill {
+
+MockNetlinkManager::MockNetlinkManager() {}
+MockNetlinkManager::~MockNetlinkManager() {}
+
+} // namespace shill
diff --git a/mock_netlink_manager.h b/mock_netlink_manager.h
index 1e2cf7a..6717e68 100644
--- a/mock_netlink_manager.h
+++ b/mock_netlink_manager.h
@@ -5,19 +5,39 @@
#ifndef SHILL_MOCK_NETLINK_MANAGER_H_
#define SHILL_MOCK_NETLINK_MANAGER_H_
-#include <gmock/gmock.h>
-
#include "shill/netlink_manager.h"
+#include <map>
+
+#include <gmock/gmock.h>
+
namespace shill {
+class NetlinkMessage;
+
class MockNetlinkManager : public NetlinkManager {
public:
- MockNetlinkManager() {}
- ~MockNetlinkManager() {}
+ MockNetlinkManager();
+ virtual ~MockNetlinkManager();
+ MOCK_METHOD1(RemoveBroadcastHandler,
+ bool(const NetlinkMessageHandler &message_handler));
+ MOCK_METHOD1(AddBroadcastHandler,
+ bool(const NetlinkMessageHandler &messge_handler));
+ MOCK_METHOD2(SendMessage,
+ bool(NetlinkMessage *message,
+ const NetlinkMessageHandler &message_handler));
+ MOCK_METHOD2(SubscribeToEvents,
+ bool(const std::string &family, const std::string &group));
+ MOCK_CONST_METHOD1(GetMessageType, uint16_t(const std::string &name));
- MOCK_METHOD2(SendMessage, bool(NetlinkMessage *message,
- const NetlinkMessageHandler &message_handler));
+ void SetMessageType(std::string name, uint16_t type);
+
+ private:
+ uint16_t DoGetMessageType(std::string name);
+
+ std::map<std::string, uint16_t> message_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockNetlinkManager);
};
} // namespace shill
diff --git a/mock_scan_session.cc b/mock_scan_session.cc
new file mode 100644
index 0000000..2791e5d
--- /dev/null
+++ b/mock_scan_session.cc
@@ -0,0 +1,39 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/mock_scan_session.h"
+
+#include <gmock/gmock.h>
+
+#include "shill/wifi_provider.h"
+
+namespace shill {
+
+MockScanSession::MockScanSession(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)
+ : ScanSession(netlink_manager,
+ dispatcher,
+ previous_frequencies,
+ available_frequencies,
+ ifindex,
+ fractions,
+ min_frequencies,
+ max_frequencies,
+ on_scan_failed) {
+ ON_CALL(*this, HasMoreFrequencies()).WillByDefault(testing::Return(true));
+}
+
+MockScanSession::~MockScanSession() {}
+
+
+} // namespace shill
diff --git a/mock_scan_session.h b/mock_scan_session.h
new file mode 100644
index 0000000..71ca9f9
--- /dev/null
+++ b/mock_scan_session.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MOCK_SCAN_SESSION_H_
+#define SHILL_MOCK_SCAN_SESSION_H_
+
+#include "shill/scan_session.h"
+
+#include <set>
+
+#include <gmock/gmock.h>
+
+#include "shill/wifi_provider.h"
+
+namespace shill {
+
+class ByteString;
+class EventDispatcher;
+class NetlinkManager;
+
+class MockScanSession : public ScanSession {
+ public:
+ MockScanSession(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 ~MockScanSession();
+
+ MOCK_CONST_METHOD0(HasMoreFrequencies, bool());
+ MOCK_METHOD1(AddSsid, void(const ByteString &ssid));
+ MOCK_METHOD0(InitiateScan, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockScanSession);
+};
+
+} // namespace shill
+
+#endif // SHILL_MOCK_SCAN_SESSION_H_
diff --git a/netlink_attribute.cc b/netlink_attribute.cc
index 0ab8db4..5eb84df 100644
--- a/netlink_attribute.cc
+++ b/netlink_attribute.cc
@@ -6,6 +6,7 @@
#include <netlink/attr.h>
+#include <map>
#include <string>
#include <base/format_macros.h>
diff --git a/netlink_manager.cc b/netlink_manager.cc
index cef93e3..6abd857 100644
--- a/netlink_manager.cc
+++ b/netlink_manager.cc
@@ -256,7 +256,7 @@
return NetlinkMessage::kIllegalMessageType;
}
-uint16_t NetlinkManager::GetMessageType(string name) const {
+uint16_t NetlinkManager::GetMessageType(const string &name) const {
map<const string, MessageType>::const_iterator family =
message_types_.find(name);
if (family == message_types_.end()) {
diff --git a/netlink_manager.h b/netlink_manager.h
index a65ad6b..c0ef9c6 100644
--- a/netlink_manager.h
+++ b/netlink_manager.h
@@ -143,17 +143,19 @@
// Retrieves a family id (message type) given the |name| string describing
// the message family.
- uint16_t GetMessageType(std::string name) const;
+ virtual uint16_t GetMessageType(const std::string &name) const;
// Install a NetlinkManager NetlinkMessageHandler. The handler is a
// user-supplied object to be called by the system for user-bound messages
// that do not have a corresponding messaage-specific callback.
// |AddBroadcastHandler| should be called before |SubscribeToEvents| since
// the result of this call are used for that call.
- bool AddBroadcastHandler(const NetlinkMessageHandler &messge_handler);
+ virtual bool AddBroadcastHandler(
+ const NetlinkMessageHandler &message_handler);
// Uninstall a NetlinkMessage Handler.
- bool RemoveBroadcastHandler(const NetlinkMessageHandler &message_handler);
+ virtual bool RemoveBroadcastHandler(
+ const NetlinkMessageHandler &message_handler);
// Determines whether a handler is in the list of broadcast handlers.
bool FindBroadcastHandler(const NetlinkMessageHandler &message_handler) const;
@@ -173,7 +175,8 @@
// Sign-up to receive and log multicast events of a specific type (once wifi
// is up).
- bool SubscribeToEvents(const std::string &family, const std::string &group);
+ virtual bool SubscribeToEvents(const std::string &family,
+ const std::string &group);
// Gets the next sequence number for a NetlinkMessage to be sent over
// NetlinkManager's netlink socket.
diff --git a/netlink_message.h b/netlink_message.h
index 5b059e1..3815bb7 100644
--- a/netlink_message.h
+++ b/netlink_message.h
@@ -123,6 +123,8 @@
static const uint16_t kMessageType;
ErrorAckMessage() : NetlinkMessage(kMessageType), error_(0) {}
+ explicit ErrorAckMessage(uint32_t err)
+ : NetlinkMessage(kMessageType), error_(err) {}
virtual bool InitFromNlmsg(const nlmsghdr *const_msg);
virtual ByteString Encode(uint32_t sequence_number);
virtual void Print(int header_log_level, int detail_log_level) const;
diff --git a/netlink_message_matchers.h b/netlink_message_matchers.h
new file mode 100644
index 0000000..50d403d
--- /dev/null
+++ b/netlink_message_matchers.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_NETLINK_MESSAGE_MATCHERS_H_
+#define SHILL_NETLINK_MESSAGE_MATCHERS_H_
+
+#include <gmock/gmock.h>
+
+#include "shill/logging.h"
+#include "shill/netlink_message.h"
+#include "shill/nl80211_message.h"
+
+namespace shill {
+
+// Given a netlink message, verifies that it is an Nl80211Message and verifies,
+// further that it is the specified command.
+MATCHER_P2(IsNl80211Command, nl80211_message_type, command, "") {
+ if (!arg) {
+ LOG(INFO) << "Null message";
+ return false;
+ }
+ if (arg->message_type() != nl80211_message_type) {
+ LOG(INFO) << "Not an nl80211 message";
+ return false;
+ }
+ const Nl80211Message *msg = dynamic_cast<const Nl80211Message *>(arg);
+ if (msg->command() != command) {
+ LOG(INFO) << "Not a message of type " << command
+ << " (it's a " << +msg->command() << ")";
+ return false;
+ }
+ return true;
+}
+
+// Verifies that a NetlinkMessage is an NL80211_CMD_TRIGGER_SCAN message that
+// contains exactly one SSID along with the requisite empty one.
+MATCHER_P(HasHiddenSSID, nl80211_message_type, "") {
+ if (!arg) {
+ LOG(INFO) << "Null message";
+ return false;
+ }
+ if (arg->message_type() != nl80211_message_type) {
+ LOG(INFO) << "Not an nl80211 message";
+ return false;
+ }
+ const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg);
+ if (msg->command() != NL80211_CMD_TRIGGER_SCAN) {
+ LOG(INFO) << "Not a NL80211_CMD_TRIGGER_SCAN message";
+ return false;
+ }
+ AttributeListConstRefPtr ssids;
+ if (!msg->const_attributes()->ConstGetNestedAttributeList(
+ NL80211_ATTR_SCAN_SSIDS, &ssids)) {
+ LOG(INFO) << "No SSID list in message";
+ return false;
+ }
+ ByteString ssid;
+ AttributeIdIterator ssid_iter(*ssids);
+ if (!ssids->GetRawAttributeValue(ssid_iter.GetId(), &ssid)) {
+ LOG(INFO) << "SSID list contains no (hidden) SSIDs";
+ return false;
+ }
+
+ // A valid Scan containing a single hidden SSID should contain
+ // two SSID entries: one containing the SSID we are looking for,
+ // and an empty entry, signifying that we also want to do a
+ // broadcast probe request for all non-hidden APs as well.
+ ByteString empty_ssid;
+ if (ssid_iter.AtEnd()) {
+ LOG(INFO) << "SSID list doesn't contain an empty SSIDs (but should)";
+ return false;
+ }
+ ssid_iter.Advance();
+ if (!ssids->GetRawAttributeValue(ssid_iter.GetId(), &empty_ssid) ||
+ !empty_ssid.IsEmpty()) {
+ LOG(INFO) << "SSID list doesn't contain an empty SSID (but should)";
+ return false;
+ }
+
+ return true;
+}
+
+// Verifies that a NetlinkMessage is an NL80211_CMD_TRIGGER_SCAN message that
+// contains no SSIDs.
+MATCHER_P(HasNoHiddenSSID, nl80211_message_type, "") {
+ if (!arg) {
+ LOG(INFO) << "Null message";
+ return false;
+ }
+ if (arg->message_type() != nl80211_message_type) {
+ LOG(INFO) << "Not an nl80211 message";
+ return false;
+ }
+ const Nl80211Message *msg = reinterpret_cast<const Nl80211Message *>(arg);
+ if (msg->command() != NL80211_CMD_TRIGGER_SCAN) {
+ LOG(INFO) << "Not a NL80211_CMD_TRIGGER_SCAN message";
+ return false;
+ }
+ AttributeListConstRefPtr ssids;
+ if (!msg->const_attributes()->ConstGetNestedAttributeList(
+ NL80211_ATTR_SCAN_SSIDS, &ssids)) {
+ return true;
+ }
+ AttributeIdIterator ssid_iter(*ssids);
+ if (ssid_iter.AtEnd()) {
+ return true;
+ }
+
+ LOG(INFO) << "SSID list contains at least one (hidden) SSID";
+ return false;
+}
+
+} // namespace shill
+
+#endif // SHILL_NETLINK_MESSAGE_MATCHERS_H_
diff --git a/scan_session.cc b/scan_session.cc
index 67f2131..920c3a2 100644
--- a/scan_session.cc
+++ b/scan_session.cc
@@ -39,8 +39,8 @@
const set<uint16_t> &available_frequencies,
uint32_t ifindex,
const FractionList &fractions,
- int min_frequencies,
- int max_frequencies,
+ size_t min_frequencies,
+ size_t max_frequencies,
OnScanFailed on_scan_failed)
: weak_ptr_factory_(this),
netlink_manager_(netlink_manager),
@@ -55,7 +55,7 @@
min_frequencies_(min_frequencies),
max_frequencies_(max_frequencies),
on_scan_failed_(on_scan_failed),
- scan_tries_left_(kScanRetryCount + 1) {
+ scan_tries_left_(kScanRetryCount) {
sort(frequency_list_.begin(), frequency_list_.end(),
&ScanSession::CompareFrequencyCount);
// Add to |frequency_list_| all the frequencies from |available_frequencies|
@@ -130,6 +130,10 @@
}
void ScanSession::DoScan(const vector<uint16_t> &scan_frequencies) {
+ if (scan_frequencies.empty()) {
+ LOG(INFO) << "Not sending empty frequency list";
+ return;
+ }
TriggerScanMessage trigger_scan;
trigger_scan.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
wifi_interface_index_);
@@ -193,14 +197,16 @@
LOG(ERROR) << __func__ << ": Message failed: "
<< error_ack_message->ToString();
if (error_ack_message->error() == EBUSY) {
- if (--scan_tries_left_ == 0) {
+ 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;
+ scan_tries_left_ = kScanRetryCount;
return;
}
- SLOG(WiFi, 3) << __func__ << " - trying again";
+ --scan_tries_left_;
+ SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_
+ << " remaining after this)";
dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
weak_ptr_factory_.GetWeakPtr()),
kScanRetryDelayMilliseconds);
diff --git a/scan_session.h b/scan_session.h
index 5d897af..258dd3d 100644
--- a/scan_session.h
+++ b/scan_session.h
@@ -99,18 +99,18 @@
const std::set<uint16_t> &available_frequencies,
uint32_t ifindex,
const FractionList &fractions,
- int min_frequencies,
- int max_frequencies,
+ size_t min_frequencies,
+ size_t max_frequencies,
OnScanFailed on_scan_failed);
virtual ~ScanSession();
// Returns true if |ScanSession| contains unscanned frequencies.
- bool HasMoreFrequencies() const;
+ virtual bool HasMoreFrequencies() const;
// Adds an SSID to the list of things for which to scan. Useful for hidden
// SSIDs.
- void AddSsid(const ByteString &ssid);
+ virtual 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
@@ -125,6 +125,11 @@
private:
friend class ScanSessionTest;
+ friend class WiFiObjectTest; // OnTriggerScanResponse.
+ FRIEND_TEST(ScanSessionTest, EBusy);
+ FRIEND_TEST(ScanSessionTest, OnError);
+ FRIEND_TEST(ScanSessionTest, OnTriggerScanResponse);
+
// 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
diff --git a/scan_session_unittest.cc b/scan_session_unittest.cc
index 4022416..b55cce2 100644
--- a/scan_session_unittest.cc
+++ b/scan_session_unittest.cc
@@ -4,15 +4,21 @@
#include "shill/scan_session.h"
+#include <errno.h>
+
#include <limits>
#include <set>
#include <vector>
+#include <base/memory/weak_ptr.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include "shill/mock_event_dispatcher.h"
#include "shill/mock_netlink_manager.h"
#include "shill/netlink_manager.h"
+#include "shill/netlink_message_matchers.h"
+#include "shill/nl80211_message.h"
using std::set;
using std::vector;
@@ -50,11 +56,13 @@
kExpectedFreq2412
};
+static const uint16_t kNl80211FamilyId = 0x13;
+
class ScanSessionTest : public Test {
public:
// Test set of "all the other frequencies this device can support" in
// sorted order.
- ScanSessionTest() {
+ ScanSessionTest() : weak_ptr_factory_(this) {
WiFiProvider::FrequencyCountList connected_frequencies(
kConnectedFrequencies,
kConnectedFrequencies + arraysize(kConnectedFrequencies));
@@ -64,16 +72,16 @@
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,
+ &dispatcher_,
connected_frequencies,
unconnected_frequencies,
0,
ScanSession::FractionList(),
kArbitraryMinimum,
kArbitraryMaximum,
- null_error_handler));
+ Bind(&ScanSessionTest::OnScanError,
+ weak_ptr_factory_.GetWeakPtr())));
}
virtual std::vector<uint16_t> GetScanFrequencies(float scan_fraction,
@@ -84,13 +92,29 @@
}
ScanSession *scan_session() { return scan_session_.get(); }
- private:
- scoped_ptr<ScanSession> scan_session_;
+ void SetScanSize(size_t min_frequencies, size_t max_frequencies) {
+ scan_session_->min_frequencies_ = min_frequencies;
+ scan_session_->max_frequencies_ = max_frequencies;
+ }
+
+ size_t GetScanFrequencyCount() {
+ return arraysize(kConnectedFrequencies) +
+ arraysize(kUnconnectedFrequencies);
+ }
+
+ protected:
+ MOCK_METHOD0(OnScanError, void());
+ MockNetlinkManager *netlink_manager() { return &netlink_manager_; }
+ MockEventDispatcher *dispatcher() { return &dispatcher_; }
+
+ MockEventDispatcher dispatcher_;
MockNetlinkManager netlink_manager_;
+ scoped_ptr<ScanSession> scan_session_;
+ base::WeakPtrFactory<ScanSessionTest> weak_ptr_factory_;
};
// Test that we can get a bunch of frequencies up to a specified fraction.
-TEST_F(ScanSessionTest, FractionTest) {
+TEST_F(ScanSessionTest, Fraction) {
vector<uint16_t> result;
// Get the first 83% of the connected values.
@@ -131,7 +155,7 @@
// 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) {
+TEST_F(ScanSessionTest, TwoFractions) {
vector<uint16_t> result;
// Get the first 60% of the connected values.
@@ -173,7 +197,7 @@
// 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) {
+TEST_F(ScanSessionTest, Min) {
vector<uint16_t> result;
// Get the first 3 previously seen values.
@@ -214,7 +238,7 @@
}
// Test that we can get up to a specified maximum number of frequencies.
-TEST_F(ScanSessionTest, MaxTest) {
+TEST_F(ScanSessionTest, Max) {
vector<uint16_t> result;
// Get the first 7 values (crosses seen/unseen boundary).
@@ -247,7 +271,7 @@
// Test that we can get exactly the seen frequencies and exactly the unseen
// ones.
-TEST_F(ScanSessionTest, ExactTest) {
+TEST_F(ScanSessionTest, Exact) {
vector<uint16_t> result;
// Get the first 5 values -- exectly on the seen/unseen border.
@@ -278,8 +302,7 @@
}
// Test that we can get everything in one read.
-TEST_F(ScanSessionTest, AllOneReadTest) {
-
+TEST_F(ScanSessionTest, AllOneRead) {
vector<uint16_t> expected;
expected.push_back(kExpectedFreq5640);
expected.push_back(kExpectedFreq5600);
@@ -301,7 +324,7 @@
// Test that we can get all the previously seen frequencies (and only the
// previously seen frequencies) via the requested fraction.
-TEST_F(ScanSessionTest, EverythingFractionTest) {
+TEST_F(ScanSessionTest, EverythingFraction) {
vector<uint16_t> result;
// Get the first 100% of the connected values.
@@ -334,7 +357,7 @@
}
// Test that we can get each value individually.
-TEST_F(ScanSessionTest, IndividualReadsTest) {
+TEST_F(ScanSessionTest, IndividualReads) {
vector<uint16_t> result;
static const float kArbitraryFraction = 0.83;
@@ -410,4 +433,81 @@
}
}
+TEST_F(ScanSessionTest, OnTriggerScanResponse) {
+ Nl80211Message::SetMessageType(kNl80211FamilyId);
+
+ EXPECT_CALL(*netlink_manager(), SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_TRIGGER_SCAN), _));
+ scan_session()->InitiateScan();
+
+ EXPECT_CALL(*this, OnScanError());
+ NewScanResultsMessage not_supposed_to_get_this_message;
+ scan_session()->OnTriggerScanResponse(not_supposed_to_get_this_message);
+}
+
+TEST_F(ScanSessionTest, ExhaustFrequencies) {
+ // Set min & max scan frequency count to 1 so each scan will be of a single
+ // frequency.
+ SetScanSize(1, 1);
+
+ // Perform all the progressive scans until the frequencies are exhausted.
+ for (size_t i = 0; i < GetScanFrequencyCount(); ++i) {
+ EXPECT_TRUE(scan_session()->HasMoreFrequencies());
+ EXPECT_CALL(*netlink_manager(), SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_TRIGGER_SCAN), _));
+ scan_session()->InitiateScan();
+ }
+
+ EXPECT_FALSE(scan_session()->HasMoreFrequencies());
+ EXPECT_CALL(*netlink_manager(), SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_TRIGGER_SCAN), _))
+ .Times(0);
+ scan_session()->InitiateScan();
+}
+
+TEST_F(ScanSessionTest, OnError) {
+ Nl80211Message::SetMessageType(kNl80211FamilyId);
+
+ EXPECT_CALL(*netlink_manager(), SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_TRIGGER_SCAN), _));
+ scan_session()->InitiateScan();
+
+ EXPECT_CALL(*this, OnScanError());
+ ErrorAckMessage error_message(-EINTR);
+ scan_session()->OnTriggerScanResponse(error_message);
+}
+
+TEST_F(ScanSessionTest, EBusy) {
+ const size_t kSmallRetryNumber = 3;
+ Nl80211Message::SetMessageType(kNl80211FamilyId);
+ scan_session()->scan_tries_left_ = kSmallRetryNumber;
+
+ EXPECT_CALL(*netlink_manager(), SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_TRIGGER_SCAN), _));
+ scan_session()->InitiateScan();
+
+ ErrorAckMessage error_message(-EBUSY);
+ for (size_t i = 0; i < kSmallRetryNumber; ++i) {
+ EXPECT_CALL(*this, OnScanError()).Times(0);
+ EXPECT_CALL(*dispatcher(), PostDelayedTask(_, _));
+ scan_session()->OnTriggerScanResponse(error_message);
+ }
+
+ EXPECT_CALL(*this, OnScanError());
+ scan_session()->OnTriggerScanResponse(error_message);
+}
+
+TEST_F(ScanSessionTest, ScanHidden) {
+ scan_session_->AddSsid(ByteString("a", 1));
+ EXPECT_CALL(netlink_manager_,
+ SendMessage(HasHiddenSSID(kNl80211FamilyId), _));
+ scan_session()->InitiateScan();
+}
+
+TEST_F(ScanSessionTest, ScanNoHidden) {
+ EXPECT_CALL(netlink_manager_,
+ SendMessage(HasNoHiddenSSID(kNl80211FamilyId), _));
+ scan_session()->InitiateScan();
+}
+
} // namespace shill
diff --git a/wifi.cc b/wifi.cc
index 2e35ced..d73ed65 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -10,13 +10,16 @@
#include <string.h>
#include <algorithm>
+#include <limits>
#include <map>
#include <string>
#include <vector>
#include <base/bind.h>
-#include <base/stringprintf.h>
+#include <base/file_path.h>
+#include <base/file_util.h>
#include <base/string_util.h>
+#include <base/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include <glib.h>
@@ -35,6 +38,7 @@
#include "shill/property_accessor.h"
#include "shill/proxy_factory.h"
#include "shill/rtnl_handler.h"
+#include "shill/scan_session.h"
#include "shill/scope_logger.h"
#include "shill/shill_time.h"
#include "shill/supplicant_eap_state_handler.h"
@@ -49,6 +53,7 @@
using base::Bind;
using base::StringPrintf;
+using file_util::PathExists;
using std::map;
using std::string;
using std::vector;
@@ -72,6 +77,8 @@
const int WiFi::kFastScanIntervalSeconds = 10;
const int WiFi::kPendingTimeoutSeconds = 15;
const int WiFi::kReconnectTimeoutSeconds = 10;
+const size_t WiFi::kMinumumFrequenciesToScan = 4; // Arbitrary but > 0.
+const char WiFi::kProgressiveScanFlagFile[] = "/home/chronos/.progressive_scan";
WiFi::WiFi(ControlInterface *control_interface,
EventDispatcher *dispatcher,
@@ -105,7 +112,10 @@
bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
scan_pending_(false),
scan_interval_seconds_(kDefaultScanIntervalSeconds),
- netlink_manager_(NetlinkManager::GetInstance()) {
+ progressive_scan_enabled_(false),
+ netlink_manager_(NetlinkManager::GetInstance()),
+ min_frequencies_to_scan_(kMinumumFrequenciesToScan),
+ max_frequencies_to_scan_(std::numeric_limits<int>::max()) {
PropertyStore *store = this->mutable_store();
store->RegisterDerivedString(
flimflam::kBgscanMethodProperty,
@@ -141,6 +151,7 @@
ScopeLogger::kWiFi,
Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
CHECK(netlink_manager_);
+ progressive_scan_enabled_ = PathExists(FilePath(kProgressiveScanFlagFile));
SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
}
@@ -224,14 +235,47 @@
void WiFi::Scan(ScanType scan_type, Error */*error*/) {
LOG(INFO) << __func__;
-
- if (scan_type == kProgressiveScan)
+ // Measure scans that are supposed to be "progressive" regardless of whether
+ // we are actually doing a progressive scan.
+ if (scan_type == kProgressiveScan) {
metrics()->NotifyDeviceScanStarted(interface_index());
+ }
- // Needs to send a D-Bus message, but may be called from D-Bus
- // signal handler context (via Manager::RequestScan). So defer work
- // to event loop.
- dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
+ if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
+ SLOG(WiFi, 4) << "Doing progressive scan on " << link_name();
+ if (!scan_session_) {
+ // TODO(wdg): Perform in-depth testing to determine the best values for
+ // the different scans. chromium:235293
+ ScanSession::FractionList scan_fractions;
+ scan_fractions.push_back(.33); // First scan gets 33 percentile.
+ scan_fractions.push_back(.33); // Second scan gets 66th percentile.
+ scan_fractions.push_back(ScanSession::kAllFrequencies);
+ scan_session_.reset(
+ new ScanSession(netlink_manager_,
+ dispatcher(),
+ provider_->GetScanFrequencies(),
+ all_scan_frequencies_,
+ interface_index(),
+ scan_fractions,
+ min_frequencies_to_scan_,
+ max_frequencies_to_scan_,
+ Bind(&WiFi::OnFailedProgressiveScan,
+ weak_ptr_factory_.GetWeakPtr())));
+ for (const auto &ssid : provider_->GetHiddenSSIDList()) {
+ scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
+ }
+ }
+ dispatcher()->PostTask(
+ Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ SLOG(WiFi, 4) << "Doing full scan - progressive scan "
+ << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED");
+ // Needs to send a D-Bus message, but may be called from D-Bus
+ // signal handler context (via Manager::RequestScan). So defer work
+ // to event loop.
+ dispatcher()->PostTask(
+ Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
+ }
}
void WiFi::BSSAdded(const ::DBus::Path &path,
@@ -277,7 +321,6 @@
// may require the the registration of new D-Bus objects. And such
// registration can't be done in the context of a D-Bus signal
// handler.
- metrics()->NotifyDeviceScanFinished(interface_index());
dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
weak_ptr_factory_.GetWeakPtr()));
}
@@ -989,6 +1032,16 @@
void WiFi::ScanDoneTask() {
SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
+
+ if (scan_session_) {
+ // Post |ProgressiveScanTask| so it runs after any |BSSAddedTask|s that have
+ // been posted. This allows connections on new BSSes to be started before
+ // we decide whether to abort the progressive scan or continue scanning.
+ dispatcher()->PostTask(
+ Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ metrics()->NotifyDeviceScanFinished(interface_index());
+ }
if (need_bss_flush_) {
CHECK(supplicant_interface_proxy_ != NULL);
// Compute |max_age| relative to |resumed_at_|, to account for the
@@ -1051,6 +1104,45 @@
}
}
+void WiFi::ProgressiveScanTask() {
+ SLOG(WiFi, 2) << __func__ << " - scan requested for " << link_name();
+ if (!enabled()) {
+ LOG(INFO) << "Ignoring scan request while device is not enabled.";
+ metrics()->NotifyDeviceScanFinished(interface_index());
+ return;
+ }
+ if (!scan_session_) {
+ SLOG(WiFi, 2) << "No scan session -- returning";
+ metrics()->NotifyDeviceScanFinished(interface_index());
+ return;
+ }
+ if (!IsIdle()) {
+ SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
+ scan_session_.reset();
+ metrics()->NotifyDeviceScanFinished(interface_index());
+ return;
+ }
+ if (scan_session_->HasMoreFrequencies()) {
+ SLOG(WiFi, 2) << "Initiating a scan -- returning";
+ SetScanPending(true);
+ // After us initiating a scan, supplicant will gather the scan results and
+ // send us zero or more |BSSAdded| events followed by a |ScanDone|.
+ scan_session_->InitiateScan();
+ return;
+ }
+ LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
+ << "do a regular scan";
+ scan_session_.reset();
+ Scan(kFullScan, NULL);
+}
+
+void WiFi::OnFailedProgressiveScan() {
+ LOG(ERROR) << "Couldn't issue a scan on " << link_name()
+ << " -- doing a regular scan";
+ scan_session_.reset();
+ Scan(kFullScan, NULL);
+}
+
void WiFi::SetScanPending(bool pending) {
if (scan_pending_ != pending) {
scan_pending_ = pending;
diff --git a/wifi.h b/wifi.h
index 6639828..2dfadad 100644
--- a/wifi.h
+++ b/wifi.h
@@ -103,6 +103,7 @@
class NetlinkManager;
class NetlinkMessage;
class ProxyFactory;
+class ScanSession;
class SupplicantEAPStateHandler;
class SupplicantInterfaceProxyInterface;
class SupplicantProcessProxyInterface;
@@ -222,6 +223,8 @@
static const int kFastScanIntervalSeconds;
static const int kPendingTimeoutSeconds;
static const int kReconnectTimeoutSeconds;
+ static const size_t kMinumumFrequenciesToScan;
+ static const char kProgressiveScanFlagFile[];
// Gets the list of frequencies supported by this device.
void ConfigureScanFrequencies();
@@ -300,6 +303,10 @@
// with the reason for failure.
virtual bool RemoveNetworkForService(
const WiFiService *service, Error *error);
+ // Perform the next in a series of progressive scans.
+ void ProgressiveScanTask();
+ // Recovers from failed progressive scan.
+ void OnFailedProgressiveScan();
// Restart fast scanning after disconnection.
void RestartFastScanAttempts();
// Schedules a scan attempt at time |scan_interval_seconds_| in the
@@ -410,8 +417,12 @@
bool scan_pending_;
uint16 scan_interval_seconds_;
+ bool progressive_scan_enabled_;
NetlinkManager *netlink_manager_;
std::set<uint16_t> all_scan_frequencies_;
+ scoped_ptr<ScanSession> scan_session_;
+ size_t min_frequencies_to_scan_;
+ size_t max_frequencies_to_scan_;
DISALLOW_COPY_AND_ASSIGN(WiFi);
};
diff --git a/wifi_provider.cc b/wifi_provider.cc
index 00f8d54..b257916 100644
--- a/wifi_provider.cc
+++ b/wifi_provider.cc
@@ -508,9 +508,6 @@
// Freeze the accumulation of frequency counts when the total maxes-out.
// This ensures that no count wraps and that the relative values are
// consistent.
- // TODO(wdg): In future CL, |total_frequency_connections_| is used to
- // calculate percentiles for progressive scan. This check needs to be in
- // place so _that_ value doesn't wrap, either.
// TODO(wdg): Replace this, simple, 'forever' collection of connection
// statistics with a more clever 'aging' algorithm. crbug.com/227233
if (total_frequency_connections_ + 1 == std::numeric_limits<int64_t>::max()) {
@@ -537,4 +534,13 @@
Metrics::kMetricFrequenciesConnectedNumBuckets);
}
+WiFiProvider::FrequencyCountList WiFiProvider::GetScanFrequencies() const {
+ FrequencyCountList freq_connects_list;
+ for (const auto freq_count : connect_count_by_frequency_) {
+ freq_connects_list.push_back(FrequencyCount(freq_count.first,
+ freq_count.second));
+ }
+ return freq_connects_list;
+}
+
} // namespace shill
diff --git a/wifi_provider.h b/wifi_provider.h
index 0e7640c..79e375c 100644
--- a/wifi_provider.h
+++ b/wifi_provider.h
@@ -105,6 +105,10 @@
virtual void IncrementConnectCount(uint16 frequency_mhz);
+ // Returns a list of all of the frequencies on which this device has
+ // connected. This data is accumulated across multiple shill runs.
+ virtual FrequencyCountList GetScanFrequencies() const;
+
private:
friend class WiFiProviderTest;
FRIEND_TEST(WiFiProviderTest, FrequencyMapToStringList);
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 5077d46..bdf9fb0 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -4,10 +4,10 @@
#include "shill/wifi.h"
-#include <netinet/ether.h>
#include <linux/if.h>
-#include <sys/socket.h>
#include <linux/netlink.h> // Needs typedefs from sys/socket.h.
+#include <netinet/ether.h>
+#include <sys/socket.h>
#include <map>
#include <string>
@@ -16,10 +16,10 @@
#include <base/file_util.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_ptr.h>
-#include <base/stringprintf.h>
#include <base/string_number_conversions.h>
#include <base/string_split.h>
#include <base/string_util.h>
+#include <base/stringprintf.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus-c++/dbus.h>
#include <gmock/gmock.h>
@@ -45,6 +45,7 @@
#include "shill/mock_netlink_manager.h"
#include "shill/mock_profile.h"
#include "shill/mock_rtnl_handler.h"
+#include "shill/mock_scan_session.h"
#include "shill/mock_store.h"
#include "shill/mock_supplicant_bss_proxy.h"
#include "shill/mock_supplicant_eap_state_handler.h"
@@ -54,9 +55,12 @@
#include "shill/mock_time.h"
#include "shill/mock_wifi_provider.h"
#include "shill/mock_wifi_service.h"
+#include "shill/netlink_message_matchers.h"
#include "shill/nice_mock_control.h"
+#include "shill/nl80211_message.h"
#include "shill/property_store_unittest.h"
#include "shill/proxy_factory.h"
+#include "shill/scan_session.h"
#include "shill/technology.h"
#include "shill/wifi_endpoint.h"
#include "shill/wifi_service.h"
@@ -76,6 +80,10 @@
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
+using ::testing::MakeMatcher;
+using ::testing::Matcher;
+using ::testing::MatcherInterface;
+using ::testing::MatchResultListener;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::NotNull;
@@ -91,6 +99,15 @@
namespace shill {
+namespace {
+
+const uint16_t kNl80211FamilyId = 0x13;
+const uint16_t kRandomScanFrequency1 = 5600;
+const uint16_t kRandomScanFrequency2 = 5560;
+const uint16_t kRandomScanFrequency3 = 2422;
+
+} // namespace
+
class WiFiPropertyTest : public PropertyStoreTest {
public:
WiFiPropertyTest()
@@ -187,7 +204,6 @@
EXPECT_TRUE(device_->bgscan_method_.empty());
}
-
MATCHER_P(EndpointMatch, endpoint, "") {
return
arg->ssid() == endpoint->ssid() &&
@@ -195,6 +211,7 @@
arg->security_mode() == endpoint->security_mode();
}
+
class WiFiObjectTest : public ::testing::TestWithParam<string> {
public:
explicit WiFiObjectTest(EventDispatcher *dispatcher)
@@ -220,6 +237,7 @@
supplicant_interface_proxy_(
new NiceMock<MockSupplicantInterfaceProxy>()),
proxy_factory_(this) {
+ InstallMockScanSession();
::testing::DefaultValue< ::DBus::Path>::Set("/default/path");
ON_CALL(dhcp_provider_, CreateConfig(_, _, _, _)).
@@ -229,6 +247,9 @@
ON_CALL(proxy_factory_, CreateSupplicantNetworkProxy(_, _)).
WillByDefault(InvokeWithoutArgs(
this, &WiFiObjectTest::CreateSupplicantNetworkProxy));
+ ON_CALL(netlink_manager_,
+ GetMessageType(Nl80211Message::kMessageTypeString)).
+ WillByDefault(Return(kNl80211FamilyId));
// Transfers ownership.
manager_.dbus_manager_.reset(dbus_manager_);
@@ -236,8 +257,14 @@
wifi_->provider_ = &wifi_provider_;
wifi_->time_ = &time_;
- // TODO(wdg): Flesh out the wifi tests to include netlink_manager.
wifi_->netlink_manager_ = &netlink_manager_;
+ wifi_->progressive_scan_enabled_ = true;
+
+ // The following is only useful when a real |ScanSession| is used; it is
+ // ignored by |MockScanSession|.
+ wifi_->all_scan_frequencies_.insert(kRandomScanFrequency1);
+ wifi_->all_scan_frequencies_.insert(kRandomScanFrequency2);
+ wifi_->all_scan_frequencies_.insert(kRandomScanFrequency3);
}
virtual void SetUp() {
@@ -283,6 +310,51 @@
SetPendingService(NULL);
}
+ size_t GetScanFrequencyCount() const {
+ return wifi_->all_scan_frequencies_.size();
+ }
+
+ void SetScanSize(int min, int max) {
+ wifi_->min_frequencies_to_scan_ = min;
+ wifi_->max_frequencies_to_scan_ = max;
+ }
+
+ // This clears WiFi::scan_session_, thereby allowing WiFi::Scan to create a
+ // real scan session.
+ void ClearScanSession() {
+ wifi_->scan_session_.reset();
+ }
+
+ bool IsScanSessionNull() {
+ return !wifi_->scan_session_;
+ }
+
+ void InstallMockScanSession() {
+ WiFiProvider::FrequencyCountList previous_frequencies;
+ std::set<uint16_t> available_frequencies;
+ ScanSession::FractionList fractions;
+ ScanSession::OnScanFailed null_callback;
+ scan_session_ = new MockScanSession(&netlink_manager_,
+ event_dispatcher_,
+ previous_frequencies,
+ available_frequencies,
+ 0,
+ fractions,
+ 0,
+ 0,
+ null_callback);
+ wifi_->scan_session_.reset(scan_session_);
+ }
+
+ // Or DisableProgressiveScan()...
+ void EnableFullScan() {
+ wifi_->progressive_scan_enabled_ = false;
+ }
+
+ void OnTriggerScanResponse(const NetlinkMessage &message) {
+ wifi_->scan_session_->OnTriggerScanResponse(message);
+ }
+
protected:
typedef scoped_refptr<MockWiFiService> MockWiFiServiceRefPtr;
@@ -502,7 +574,7 @@
void FireScanTimer() {
wifi_->ScanTimerHandler();
}
- void TriggerScan() {
+ void TriggerFullScan() {
wifi_->Scan(Device::kFullScan, NULL);
}
const WiFiServiceRefPtr &GetCurrentService() {
@@ -576,6 +648,13 @@
wifi_->LinkEvent(IFF_LOWER_UP, IFF_LOWER_UP);
}
void ReportScanDone() {
+ // Eliminate |scan_session| so |ScanDoneTask| doesn't launch another scan.
+ wifi_->scan_session_.reset();
+ wifi_->ScanDoneTask();
+ // Make a new |scan_session| so that future scanning is done with the mock.
+ InstallMockScanSession();
+ }
+ void ReportScanDoneKeepScanSession() {
wifi_->ScanDoneTask();
}
void ReportCurrentBSSChanged(const string &new_bss) {
@@ -708,6 +787,7 @@
}
EventDispatcher *event_dispatcher_;
+ MockScanSession *scan_session_; // Owned by |wifi_|.
NiceMock<MockRTNLHandler> rtnl_handler_;
MockTime time_;
@@ -887,7 +967,8 @@
EXPECT_TRUE(GetCurrentService() == NULL);
}
-TEST_F(WiFiMainTest, CleanStart) {
+TEST_F(WiFiMainTest, CleanStart_FullScan) {
+ EnableFullScan();
EXPECT_CALL(*supplicant_process_proxy_, CreateInterface(_));
EXPECT_CALL(*supplicant_process_proxy_, GetInterface(_))
.Times(AnyNumber())
@@ -902,6 +983,21 @@
EXPECT_FALSE(GetScanTimer().IsCancelled());
}
+TEST_F(WiFiMainTest, CleanStart) {
+ EXPECT_CALL(*supplicant_process_proxy_, CreateInterface(_));
+ EXPECT_CALL(*supplicant_process_proxy_, GetInterface(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Throw(
+ DBus::Error(
+ "fi.w1.wpa_supplicant1.InterfaceUnknown",
+ "test threw fi.w1.wpa_supplicant1.InterfaceUnknown")));
+ EXPECT_TRUE(GetScanTimer().IsCancelled());
+ StartWiFi();
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_FALSE(GetScanTimer().IsCancelled());
+}
+
TEST_F(WiFiMainTest, ClearCachedCredentials) {
StartWiFi();
DBus::Path network = "/test/path";
@@ -963,7 +1059,8 @@
EXPECT_FALSE(RemoveNetwork(network));
}
-TEST_F(WiFiMainTest, Restart) {
+TEST_F(WiFiMainTest, Restart_FullScan) {
+ EnableFullScan();
EXPECT_CALL(*supplicant_process_proxy_, CreateInterface(_))
.Times(AnyNumber())
.WillRepeatedly(Throw(
@@ -976,13 +1073,26 @@
dispatcher_.DispatchPendingEvents();
}
+TEST_F(WiFiMainTest, Restart) {
+ EXPECT_CALL(*supplicant_process_proxy_, CreateInterface(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Throw(
+ DBus::Error(
+ "fi.w1.wpa_supplicant1.InterfaceExists",
+ "test threw fi.w1.wpa_supplicant1.InterfaceExists")));
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+}
+
TEST_F(WiFiMainTest, StartClearsState) {
EXPECT_CALL(*GetSupplicantInterfaceProxy(), RemoveAllNetworks());
EXPECT_CALL(*GetSupplicantInterfaceProxy(), FlushBSS(_));
StartWiFi();
}
-TEST_F(WiFiMainTest, NoScansWhileConnecting) {
+TEST_F(WiFiMainTest, NoScansWhileConnecting_FullScan) {
+ EnableFullScan();
StartWiFi();
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
dispatcher_.DispatchPendingEvents();
@@ -992,13 +1102,13 @@
// If we're connecting, we ignore scan requests to stay on channel.
EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
- TriggerScan();
+ TriggerFullScan();
dispatcher_.DispatchPendingEvents();
Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
Mock::VerifyAndClearExpectations(service);
EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
- TriggerScan();
+ TriggerFullScan();
dispatcher_.DispatchPendingEvents();
Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
Mock::VerifyAndClearExpectations(service);
@@ -1007,20 +1117,63 @@
SetCurrentService(service);
EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
- TriggerScan();
+ TriggerFullScan();
dispatcher_.DispatchPendingEvents();
Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
Mock::VerifyAndClearExpectations(service);
// But otherwise we'll honor the request.
EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
- TriggerScan();
+ TriggerFullScan();
dispatcher_.DispatchPendingEvents();
Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
Mock::VerifyAndClearExpectations(service);
}
-TEST_F(WiFiMainTest, ResumeStartsScanWhenIdle) {
+TEST_F(WiFiMainTest, NoScansWhileConnecting) {
+ StartWiFi();
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
+ SetPendingService(service);
+ // If we're connecting, we ignore scan requests to stay on channel.
+ EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ TriggerFullScan();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ Mock::VerifyAndClearExpectations(service);
+ EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ TriggerFullScan();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ Mock::VerifyAndClearExpectations(service);
+ // Similarly, ignore scans when our connected service is reconnecting.
+ SetPendingService(NULL);
+ SetCurrentService(service);
+ EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(true));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ TriggerFullScan();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ Mock::VerifyAndClearExpectations(service);
+ // But otherwise we'll honor the request.
+ EXPECT_CALL(*service, IsConnecting()).WillOnce(Return(false));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ TriggerFullScan();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ Mock::VerifyAndClearExpectations(service);
+}
+
+TEST_F(WiFiMainTest, ResumeStartsScanWhenIdle_FullScan) {
+ EnableFullScan();
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
StartWiFi();
dispatcher_.DispatchPendingEvents();
@@ -1032,7 +1185,21 @@
dispatcher_.DispatchPendingEvents();
}
-TEST_F(WiFiMainTest, SuspendDoesNotStartScan) {
+TEST_F(WiFiMainTest, ResumeStartsScanWhenIdle) {
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ ReportScanDone();
+ ASSERT_TRUE(wifi()->IsIdle());
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ OnAfterResume();
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, SuspendDoesNotStartScan_FullScan) {
+ EnableFullScan();
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
StartWiFi();
dispatcher_.DispatchPendingEvents();
@@ -1043,7 +1210,20 @@
dispatcher_.DispatchPendingEvents();
}
-TEST_F(WiFiMainTest, ResumeDoesNotStartScanWhenNotIdle) {
+TEST_F(WiFiMainTest, SuspendDoesNotStartScan) {
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ ASSERT_TRUE(wifi()->IsIdle());
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ OnBeforeSuspend();
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, ResumeDoesNotStartScanWhenNotIdle_FullScan) {
+ EnableFullScan();
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
StartWiFi();
dispatcher_.DispatchPendingEvents();
@@ -1058,6 +1238,22 @@
dispatcher_.DispatchPendingEvents();
}
+TEST_F(WiFiMainTest, ResumeDoesNotStartScanWhenNotIdle) {
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ Mock::VerifyAndClearExpectations(GetSupplicantInterfaceProxy());
+ WiFiServiceRefPtr service(SetupConnectedService(DBus::Path(), NULL, NULL));
+ EXPECT_FALSE(wifi()->IsIdle());
+ ScopedMockLog log;
+ EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(log, Log(_, _, EndsWith("already scanning or connected.")));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ OnAfterResume();
+ dispatcher_.DispatchPendingEvents();
+}
+
TEST_F(WiFiMainTest, ScanResults) {
EXPECT_CALL(*wifi_provider(), OnEndpointAdded(_)).Times(5);
StartWiFi();
@@ -1374,7 +1570,7 @@
}
-MATCHER_P(HasHiddenSSID, ssid, "") {
+MATCHER_P(HasHiddenSSID_FullScan, ssid, "") {
map<string, DBus::Variant>::const_iterator it =
arg.find(WPASupplicant::kPropertyScanSSIDs);
if (it == arg.end()) {
@@ -1391,28 +1587,68 @@
return ssids.size() == 2 && ssids[0] == ssid && ssids[1].empty();
}
-MATCHER(HasNoHiddenSSID, "") {
+MATCHER(HasNoHiddenSSID_FullScan, "") {
map<string, DBus::Variant>::const_iterator it =
arg.find(WPASupplicant::kPropertyScanSSIDs);
return it == arg.end();
}
-TEST_F(WiFiMainTest, ScanHidden) {
+TEST_F(WiFiMainTest, ScanHidden_FullScan) {
+ EnableFullScan();
vector<uint8_t>kSSID(1, 'a');
ByteArrays ssids;
ssids.push_back(kSSID);
StartWiFi();
EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList()).WillOnce(Return(ssids));
- EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasHiddenSSID(kSSID)));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(),
+ Scan(HasHiddenSSID_FullScan(kSSID)));
dispatcher_.DispatchPendingEvents();
}
-TEST_F(WiFiMainTest, ScanNoHidden) {
+// This test is slightly different from the test in scan_session_unittest.cc
+// because this tests the piece of WiFi that builds the SSID list.
+TEST_F(WiFiMainTest, ScanHidden) {
+ // Clear the Mock ScanSession because hidden SSIDs are added when wifi
+ // instantiates a new ScanSession (and it won't instantiate a new ScanSession
+ // if there's already one there).
+ ClearScanSession();
+ vector<uint8_t>kSSID(1, 'a');
+ ByteArrays ssids;
+ ssids.push_back(kSSID);
+
+ EXPECT_CALL(netlink_manager_, SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_GET_WIPHY), _));
+ EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList()).WillOnce(Return(ssids));
+ StartWiFi();
+ EXPECT_CALL(netlink_manager_,
+ SendMessage(HasHiddenSSID(kNl80211FamilyId), _));
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, ScanNoHidden_FullScan) {
+ EnableFullScan();
StartWiFi();
EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
.WillOnce(Return(ByteArrays()));
- EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasNoHiddenSSID()));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasNoHiddenSSID_FullScan()));
+ dispatcher_.DispatchPendingEvents();
+}
+
+// This test is slightly different from the test in scan_session_unittest.cc
+// because this tests the piece of WiFi that builds the SSID list.
+TEST_F(WiFiMainTest, ScanNoHidden) {
+ // Clear the Mock ScanSession because hidden SSIDs are added when wifi
+ // instantiates a new ScanSession (and it won't instantiate a new ScanSession
+ // if there's already one there).
+ ClearScanSession();
+ EXPECT_CALL(netlink_manager_, SendMessage(
+ IsNl80211Command(kNl80211FamilyId, NL80211_CMD_GET_WIPHY), _));
+ EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
+ .WillOnce(Return(ByteArrays()));
+ StartWiFi();
+ EXPECT_CALL(netlink_manager_,
+ SendMessage(HasNoHiddenSSID(kNl80211FamilyId), _));
dispatcher_.DispatchPendingEvents();
}
@@ -1422,6 +1658,7 @@
EXPECT_CALL(log, Log(_, _, EndsWith(
"Ignoring scan request while device is not enabled."))).Times(1);
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
StartWiFi();
StopWiFi();
// A scan is queued when WiFi resumes.
@@ -1429,6 +1666,81 @@
dispatcher_.DispatchPendingEvents();
}
+TEST_F(WiFiMainTest, ProgressiveScanFound) {
+ // Set min & max scan frequency count to 1 so each scan will be of a single
+ // frequency.
+ SetScanSize(1, 1);
+
+ // Do the first scan (finds nothing).
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDoneKeepScanSession();
+
+ // Do the second scan (connects afterwards).
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDoneKeepScanSession();
+
+ // Connect after second scan.
+ MockWiFiServiceRefPtr service = MakeMockService(flimflam::kSecurityNone);
+ SetPendingService(service);
+
+ // Verify that the third scan aborts and there is no further scan.
+ ScopedMockLog log;
+ EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(log, Log(_, _, EndsWith(
+ "Ignoring scan request while connecting to an AP."))).Times(1);
+ EXPECT_CALL(*metrics(), NotifyDeviceScanFinished(_));
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, ProgressiveScanNotFound) {
+ // Set min & max scan frequency count to 1 so each scan will be of a single
+ // frequency.
+ SetScanSize(1, 1);
+
+ // Do the first scan (finds nothing).
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDoneKeepScanSession();
+
+ // Do the second scan (finds nothing).
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDoneKeepScanSession();
+
+ // Do the third scan. After (simulated) exhausting of search frequencies,
+ // verify that this scan uses supplicant rather than internal (progressive)
+ // scan.
+ EXPECT_CALL(*scan_session_, HasMoreFrequencies()).WillOnce(Return(false));
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_));
+ dispatcher_.DispatchPendingEvents();
+
+ // And verify that ScanDone reports a complete scan (i.e., the
+ // wifi_::scan_session_ has truly been cleared).
+ EXPECT_CALL(*metrics(), NotifyDeviceScanFinished(_));
+ ReportScanDoneKeepScanSession();
+}
+
+TEST_F(WiFiMainTest, ProgressiveScanError) {
+ // Clear the Mock ScanSession so I can get an actual ScanSession.
+ ClearScanSession();
+ StartWiFi();
+
+ // This should call |Scan| which posts |ScanTask|
+ NewScanResultsMessage not_supposed_to_get_this_message;
+ OnTriggerScanResponse(not_supposed_to_get_this_message);
+
+ EXPECT_TRUE(IsScanSessionNull());
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(1);
+ dispatcher_.DispatchPendingEvents();
+}
+
TEST_F(WiFiMainTest, InitialSupplicantState) {
EXPECT_EQ(WiFi::kInterfaceStateUnknown, GetSupplicantState());
}
@@ -1746,7 +2058,8 @@
ReportScanDone();
}
-TEST_F(WiFiMainTest, ScanTimerIdle) {
+TEST_F(WiFiMainTest, ScanTimerIdle_FullScan) {
+ EnableFullScan();
StartWiFi();
dispatcher_.DispatchPendingEvents();
ReportScanDone();
@@ -1759,6 +2072,18 @@
EXPECT_FALSE(GetScanTimer().IsCancelled()); // Automatically re-armed.
}
+TEST_F(WiFiMainTest, ScanTimerIdle) {
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDone();
+ CancelScanTimer();
+ EXPECT_TRUE(GetScanTimer().IsCancelled());
+ EXPECT_CALL(*scan_session_, InitiateScan());
+ FireScanTimer();
+ dispatcher_.DispatchPendingEvents();
+ EXPECT_FALSE(GetScanTimer().IsCancelled()); // Automatically re-armed.
+}
+
TEST_F(WiFiMainTest, ScanTimerScanning) {
StartWiFi();
dispatcher_.DispatchPendingEvents();
@@ -1768,6 +2093,7 @@
// Should not call Scan, since we're already scanning.
// (Scanning is triggered by StartWiFi.)
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
FireScanTimer();
dispatcher_.DispatchPendingEvents();
EXPECT_FALSE(GetScanTimer().IsCancelled()); // Automatically re-armed.
@@ -1782,6 +2108,7 @@
EXPECT_TRUE(GetScanTimer().IsCancelled());
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
FireScanTimer();
dispatcher_.DispatchPendingEvents();
EXPECT_FALSE(GetScanTimer().IsCancelled()); // Automatically re-armed.
@@ -1813,7 +2140,8 @@
EXPECT_TRUE(GetScanTimer().IsCancelled());
}
-TEST_F(WiFiMainTest, ScanOnDisconnectWithHidden) {
+TEST_F(WiFiMainTest, ScanOnDisconnectWithHidden_FullScan) {
+ EnableFullScan();
StartWiFi();
dispatcher_.DispatchPendingEvents();
SetupConnectedService(DBus::Path(), NULL, NULL);
@@ -1822,7 +2150,23 @@
ssids.push_back(kSSID);
EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
.WillRepeatedly(Return(ssids));
- EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(HasHiddenSSID(kSSID)));
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(),
+ Scan(HasHiddenSSID_FullScan(kSSID)));
+ ReportCurrentBSSChanged(WPASupplicant::kCurrentBSSNull);
+ dispatcher_.DispatchPendingEvents();
+}
+
+TEST_F(WiFiMainTest, ScanOnDisconnectWithHidden) {
+ StartWiFi();
+ dispatcher_.DispatchPendingEvents();
+ ReportScanDone();
+ SetupConnectedService(DBus::Path(), NULL, NULL);
+ vector<uint8_t>kSSID(1, 'a');
+ ByteArrays ssids;
+ ssids.push_back(kSSID);
+ EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
+ .WillRepeatedly(Return(ssids));
+ EXPECT_CALL(*scan_session_, InitiateScan());
ReportCurrentBSSChanged(WPASupplicant::kCurrentBSSNull);
dispatcher_.DispatchPendingEvents();
}
@@ -1832,6 +2176,7 @@
dispatcher_.DispatchPendingEvents();
SetupConnectedService(DBus::Path(), NULL, NULL);
EXPECT_CALL(*GetSupplicantInterfaceProxy(), Scan(_)).Times(0);
+ EXPECT_CALL(*scan_session_, InitiateScan()).Times(0);
EXPECT_CALL(*wifi_provider(), GetHiddenSSIDList())
.WillRepeatedly(Return(ByteArrays()));
ReportCurrentBSSChanged(WPASupplicant::kCurrentBSSNull);