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(