shill: WiFi: Add LinkMonitor
Use new wpa_supplicant DBus interface call "Reassociate" when
the LinkMonitor indicates failure.
CQ-DEPENDS=Ic9fa6c577808e3d753175d8cba8acbe3463eb1ee
BUG=chromium-os:32600
TEST=Unit tests; manual: With verbosity turned up I can see the
link monitor successfully receiving ARP replies and adding
response samples. Both failures and samples appear in
chrome://histograms.
Change-Id: I059b8dc5ebb6cfec4dd715090a8bddb8daf86eae
Reviewed-on: https://gerrit.chromium.org/gerrit/29794
Commit-Ready: Paul Stewart <pstew@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/dbus_bindings/supplicant-interface.xml b/dbus_bindings/supplicant-interface.xml
index 0387de9..e078c68 100644
--- a/dbus_bindings/supplicant-interface.xml
+++ b/dbus_bindings/supplicant-interface.xml
@@ -15,6 +15,8 @@
<arg name="args" type="a{sv}" direction="in"/>
<arg name="network" type="o" direction="out"/>
</method>
+ <method name="Reassociate">
+ </method>
<method name="RemoveNetwork">
<arg name="network" type="o" direction="in"/>
</method>
diff --git a/doc/device-api.txt b/doc/device-api.txt
index bd83c5a..3b5fa62 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -417,9 +417,8 @@
int32 LinkMonitorResponseTime [readonly]
Latest running average of the link monitor response
- time for this device in milliseconds, if the link
- monitor is active. This property is only visible
- if a link monitor is active on this device.
+ for this device in milliseconds, if the link monitor
+ is active.
string Name [readonly]
diff --git a/mock_supplicant_interface_proxy.h b/mock_supplicant_interface_proxy.h
index 3a44466..d043ba8 100644
--- a/mock_supplicant_interface_proxy.h
+++ b/mock_supplicant_interface_proxy.h
@@ -26,6 +26,7 @@
MOCK_METHOD0(ClearCachedCredentials, void());
MOCK_METHOD0(Disconnect, void());
MOCK_METHOD1(FlushBSS, void(const uint32_t &age));
+ MOCK_METHOD0(Reassociate, void());
MOCK_METHOD0(RemoveAllNetworks, void());
MOCK_METHOD1(RemoveNetwork, void(const ::DBus::Path &network));
MOCK_METHOD1(Scan,
diff --git a/supplicant_interface_proxy.cc b/supplicant_interface_proxy.cc
index 4367070..807ccf0 100644
--- a/supplicant_interface_proxy.cc
+++ b/supplicant_interface_proxy.cc
@@ -68,6 +68,16 @@
}
}
+void SupplicantInterfaceProxy::Reassociate() {
+ SLOG(DBus, 2) << __func__;
+ try {
+ return proxy_.Reassociate();
+ } catch (const DBus::Error &e) {
+ LOG(ERROR) << "DBus exception: " << e.name() << ": " << e.what();
+ throw; // Re-throw the exception.
+ }
+}
+
void SupplicantInterfaceProxy::RemoveAllNetworks() {
SLOG(DBus, 2) << __func__;
try {
diff --git a/supplicant_interface_proxy.h b/supplicant_interface_proxy.h
index 95da0d2..c55910e 100644
--- a/supplicant_interface_proxy.h
+++ b/supplicant_interface_proxy.h
@@ -32,6 +32,7 @@
virtual void ClearCachedCredentials();
virtual void Disconnect();
virtual void FlushBSS(const uint32_t &age);
+ virtual void Reassociate();
virtual void RemoveAllNetworks();
virtual void RemoveNetwork(const ::DBus::Path &network);
virtual void Scan(
diff --git a/supplicant_interface_proxy_interface.h b/supplicant_interface_proxy_interface.h
index bf60d04..1034f44 100644
--- a/supplicant_interface_proxy_interface.h
+++ b/supplicant_interface_proxy_interface.h
@@ -23,6 +23,7 @@
virtual void ClearCachedCredentials() = 0;
virtual void Disconnect() = 0;
virtual void FlushBSS(const uint32_t &age) = 0;
+ virtual void Reassociate() = 0;
virtual void RemoveAllNetworks() = 0;
virtual void RemoveNetwork(const ::DBus::Path &network) = 0;
virtual void Scan(
diff --git a/wifi.cc b/wifi.cc
index 414d433..33a84b5 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -29,6 +29,7 @@
#include "shill/event_dispatcher.h"
#include "shill/key_value_store.h"
#include "shill/ieee80211.h"
+#include "shill/link_monitor.h"
#include "shill/logging.h"
#include "shill/manager.h"
#include "shill/metrics.h"
@@ -1264,6 +1265,30 @@
return changed;
}
+void WiFi::OnLinkMonitorFailure() {
+ // If we have never found the gateway, let's be conservative and not
+ // do anything, in case this network topology does not have a gateway.
+ if (!link_monitor()->IsGatewayFound()) {
+ LOG(INFO) << "In " << __func__ << "(): "
+ << "Skipping reassociate since gateway was never found.";
+ return;
+ }
+
+ if (!supplicant_present_) {
+ LOG(ERROR) << "In " << __func__ << "(): "
+ << "wpa_supplicant is not present. Cannot reassociate.";
+ return;
+ }
+
+ try {
+ supplicant_interface_proxy_->Reassociate();
+ LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
+ } catch (const DBus::Error &e) { // NOLINT
+ LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
+ return;
+ }
+}
+
void WiFi::HelpRegisterDerivedInt32(
PropertyStore *store,
const string &name,
diff --git a/wifi.h b/wifi.h
index b51e1ae..a5baa1a 100644
--- a/wifi.h
+++ b/wifi.h
@@ -157,12 +157,16 @@
// characters were changed.
static bool SanitizeSSID(std::string *ssid);
+ // Called by Linkmonitor (overriden from Device superclass).
+ virtual void OnLinkMonitorFailure();
+
private:
friend class WiFiObjectTest; // access to supplicant_*_proxy_, link_up_
friend class WiFiTimerTest; // kNumFastScanAttempts, kFastScanIntervalSeconds
FRIEND_TEST(WiFiMainTest, AppendBgscan);
FRIEND_TEST(WiFiMainTest, FlushBSSOnResume); // kMaxBSSResumeAgeSeconds
FRIEND_TEST(WiFiMainTest, InitialSupplicantState); // kInterfaceStateUnknown
+ FRIEND_TEST(WiFiMainTest, LinkMonitorFailure); // set_link_monitor()
FRIEND_TEST(WiFiMainTest, ScanResults); // EndpointMap
FRIEND_TEST(WiFiMainTest, ScanResultsWithUpdates); // EndpointMap
FRIEND_TEST(WiFiMainTest, Stop); // weak_ptr_factory_
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 7826c2f..ec8a9e7 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -37,6 +37,7 @@
#include "shill/mock_dhcp_config.h"
#include "shill/mock_dhcp_provider.h"
#include "shill/mock_event_dispatcher.h"
+#include "shill/mock_link_monitor.h"
#include "shill/mock_log.h"
#include "shill/mock_manager.h"
#include "shill/mock_metrics.h"
@@ -557,6 +558,14 @@
wifi_->StopReconnectTimer();
}
+ void SetLinkMonitor(LinkMonitor *link_monitor) {
+ wifi_->set_link_monitor(link_monitor);
+ }
+
+ void OnLinkMonitorFailure() {
+ wifi_->OnLinkMonitorFailure();
+ }
+
NiceMockControl *control_interface() {
return &control_interface_;
}
@@ -2173,6 +2182,29 @@
dispatcher_.DispatchPendingEvents();
}
+TEST_F(WiFiMainTest, LinkMonitorFailure) {
+ StartWiFi();
+ ScopedMockLog log;
+ EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
+ MockLinkMonitor *link_monitor = new StrictMock<MockLinkMonitor>();
+ SetLinkMonitor(link_monitor);
+ EXPECT_CALL(*link_monitor, IsGatewayFound())
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(log, Log(logging::LOG_INFO, _,
+ EndsWith("gateway was never found."))).Times(1);
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Reassociate()).Times(0);
+ OnLinkMonitorFailure();
+ EXPECT_CALL(log, Log(logging::LOG_INFO, _,
+ EndsWith("Called Reassociate()."))).Times(1);
+ EXPECT_CALL(*GetSupplicantInterfaceProxy(), Reassociate()).Times(1);
+ OnLinkMonitorFailure();
+ OnSupplicantVanish();
+ EXPECT_CALL(log, Log(logging::LOG_ERROR, _,
+ EndsWith("Cannot reassociate."))).Times(1);
+ OnLinkMonitorFailure();
+}
+
TEST_F(WiFiMainTest, SuspectCredentialsOpen) {
Error e;
WiFiServiceRefPtr service = GetOpenService(