shill: acquire ip addresses for wifi (via dhcp), and signal when
the service is ready

BUG=chromium-os:20191
TEST=unittest, network_WiFiManager/000_SSID_Length_Limit

note: with this CL, 000_SSID_Length_Limit connects to the
open network, but fails on the hidden network. hidden network
support requires shill to implement Manager.GetService.

Change-Id: I71d1634fb3fc48de36fd2bf828969bfe0b3a46f6
Reviewed-on: http://gerrit.chromium.org/gerrit/7466
Tested-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
diff --git a/cellular_service.h b/cellular_service.h
index c1636a8..2e464ee 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -60,9 +60,6 @@
   const std::string &roaming_state() const { return roaming_state_; }
   void set_roaming_state(const std::string &state) { roaming_state_ = state; }
 
- protected:
-  virtual std::string CalculateState() { return "idle"; }
-
  private:
   static const char kServiceType[];
 
diff --git a/ethernet_service.h b/ethernet_service.h
index d0dbf0b..88aa8b5 100644
--- a/ethernet_service.h
+++ b/ethernet_service.h
@@ -32,9 +32,6 @@
   // ethernet_<MAC>
   virtual std::string GetStorageIdentifier(const std::string &mac);
 
- protected:
-  virtual std::string CalculateState() { return "idle"; }
-
  private:
   static const char kServiceType[];
 
diff --git a/mock_dhcp_provider.h b/mock_dhcp_provider.h
index 6fd9aa7..47b12fe 100644
--- a/mock_dhcp_provider.h
+++ b/mock_dhcp_provider.h
@@ -8,7 +8,9 @@
 #include <base/basictypes.h>
 #include <gmock/gmock.h>
 
+#include "shill/dhcp_config.h"
 #include "shill/dhcp_provider.h"
+#include "shill/refptr_types.h"
 
 namespace shill {
 
diff --git a/service.cc b/service.cc
index 2740065..d1ed9a4 100644
--- a/service.cc
+++ b/service.cc
@@ -176,7 +176,7 @@
     failure_ = kFailureUnknown;
   }
   manager_->UpdateService(this);
-  // TODO(pstew): Broadcast property change notification via control interface
+  adaptor_->EmitStringChanged(flimflam::kStateProperty, CalculateState());
 }
 
 void Service::SetFailure(ConnectFailure failure) {
@@ -265,6 +265,16 @@
 
 void Service::set_profile(const ProfileRefPtr &p) { profile_ = p; }
 
+string Service::CalculateState() {
+  switch (state_) {
+    case kStateConnected:
+      return flimflam::kStateReady;
+    default:
+      // TODO(quiche): provide strings for other states
+      return flimflam::kStateIdle;
+  }
+}
+
 void Service::HelpRegisterDerivedBool(const string &name,
                                       bool(Service::*get)(void),
                                       bool(Service::*set)(const bool&)) {
diff --git a/service.h b/service.h
index 8ad5132..836d38e 100644
--- a/service.h
+++ b/service.h
@@ -139,7 +139,7 @@
   // Returns true if a character is allowed to be in a service storage id.
   static bool LegalChar(char a) { return isalnum(a) || a == '_'; }
 
-  virtual std::string CalculateState() = 0;
+  virtual std::string CalculateState();
 
   void HelpRegisterDerivedBool(const std::string &name,
                                bool(Service::*get)(void),
diff --git a/service_unittest.cc b/service_unittest.cc
index f90f162..7d7d4fb 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -233,6 +233,10 @@
   EXPECT_EQ(Service::kFailureUnknown, service_->failure());
 
   ServiceConstRefPtr service_ref(service_);
+
+  // TODO(quiche): make this EXPECT_CALL work (crosbug.com/20154)
+  // EXPECT_CALL(*dynamic_cast<ServiceMockAdaptor *>(service_->adaptor_.get()),
+  //     EmitStringChanged(flimflam::kStateProperty, _));
   EXPECT_CALL(mock_manager_, UpdateService(service_ref));
   service_->SetState(Service::kStateConnected);
   // A second state change shouldn't cause another update
diff --git a/wifi.cc b/wifi.cc
index e712ca2..4ea6547 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -7,6 +7,8 @@
 #include <time.h>
 #include <stdio.h>
 #include <string.h>
+#include <netinet/ether.h>
+#include <linux/if.h>
 
 #include <map>
 #include <string>
@@ -58,7 +60,8 @@
       bgscan_short_interval_(0),
       bgscan_signal_threshold_(0),
       scan_pending_(false),
-      scan_interval_(0) {
+      scan_interval_(0),
+      link_up_(false) {
   PropertyStore *store = this->store();
   store->RegisterString(flimflam::kBgscanMethodProperty, &bgscan_method_);
   store->RegisterUint16(flimflam::kBgscanShortIntervalProperty,
@@ -147,6 +150,26 @@
   return type == Device::kWifi;
 }
 
+void WiFi::LinkEvent(unsigned int flags, unsigned int change) {
+  // TODO(quiche): figure out how to relate these events to supplicant
+  // events. e.g., may be we can ignore LinkEvent, in favor of events
+  // from SupplicantInterfaceProxy?
+  Device::LinkEvent(flags, change);
+  if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
+    LOG(INFO) << link_name() << " is up; should start L3!";
+    link_up_ = true;
+    if (AcquireDHCPConfig()) {
+      SetServiceState(Service::kStateConfiguring);
+    } else {
+      LOG(ERROR) << "Unable to acquire DHCP config.";
+    }
+  } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
+    link_up_ = false;
+    // TODO(quiche): attempt to reconnect to current SSID, another SSID,
+    // or initiate a scan.
+  }
+}
+
 void WiFi::BSSAdded(
     const ::DBus::Path &BSS,
     const std::map<string, ::DBus::Variant> &properties) {
@@ -178,7 +201,7 @@
   DBus::MessageIter writer;
   DBus::Path network_path;
 
-  // TODO(quiche): if already connected, disconnect
+  // TODO(quiche): handle cases where already connected
 
   add_network_args[kSupplicantPropertyNetworkMode].writer().
       append_uint32(WiFiEndpoint::ModeStringToUint(service->mode()));
@@ -194,6 +217,14 @@
       supplicant_interface_proxy_->AddNetwork(add_network_args);
   supplicant_interface_proxy_->SelectNetwork(network_path);
   // XXX add to favorite networks list?
+
+  // SelectService here (instead of in LinkEvent, like Ethernet), so
+  // that, if we fail to bring up L2, we can attribute failure correctly.
+  //
+  // TODO(quiche): when we add code for dealing with connection failures,
+  // reconsider if this is the right place to change the selected service.
+  // see discussion in crosbug.com/20191.
+  SelectService(service);
 }
 
 void WiFi::ScanDoneTask() {
diff --git a/wifi.h b/wifi.h
index c58aa8c..8128965 100644
--- a/wifi.h
+++ b/wifi.h
@@ -35,6 +35,7 @@
   virtual void Stop();
   virtual void Scan();
   virtual bool TechnologyIs(const Technology type) const;
+  virtual void LinkEvent(unsigned int flags, unsigned int change);
 
   // called by SupplicantInterfaceProxy, in response to events from
   // wpa_supplicant.
@@ -75,8 +76,9 @@
   int32 bgscan_signal_threshold_;
   bool scan_pending_;
   uint16 scan_interval_;
+  bool link_up_;
 
-  friend class WiFiMainTest;  // access to supplicant_*_proxy_ fields
+  friend class WiFiMainTest;  // access to supplicant_*_proxy_, link_up_
   DISALLOW_COPY_AND_ASSIGN(WiFi);
 };
 
diff --git a/wifi_service.h b/wifi_service.h
index 9f654ab..8e10df2 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -41,9 +41,6 @@
   const std::string &key_management() const;
   const std::vector<uint8_t> &ssid() const;
 
- protected:
-  virtual std::string CalculateState() { return "idle"; }
-
  private:
   void ConnectTask();
 
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index 2d5cd9f..5cb2c76 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -4,6 +4,9 @@
 
 #include "shill/wifi.h"
 
+#include <netinet/ether.h>
+#include <linux/if.h>
+
 #include <map>
 #include <string>
 #include <vector>
@@ -19,6 +22,8 @@
 #include "shill/dbus_adaptor.h"
 #include "shill/manager.h"
 #include "shill/mock_device.h"
+#include "shill/mock_dhcp_config.h"
+#include "shill/mock_dhcp_provider.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_supplicant_interface_proxy.h"
 #include "shill/mock_supplicant_process_proxy.h"
@@ -106,6 +111,11 @@
         supplicant_process_proxy_(new NiceMock<MockSupplicantProcessProxy>()),
         supplicant_interface_proxy_(
             new NiceMock<MockSupplicantInterfaceProxy>(wifi_)),
+        dhcp_config_(new MockDHCPConfig(&control_interface_,
+                                        &dispatcher_,
+                                        &dhcp_provider_,
+                                        kDeviceName,
+                                        &glib_)),
         proxy_factory_(this) {
     ProxyFactory::set_factory(&proxy_factory_);
     ::testing::DefaultValue< ::DBus::Path>::Set("/default/path");
@@ -117,6 +127,10 @@
     wifi_->Stop();
   }
 
+  virtual void SetUp() {
+    wifi_->set_dhcp_provider(&dhcp_provider_);
+  }
+
  protected:
   class TestProxyFactory : public ProxyFactory {
    public:
@@ -156,11 +170,17 @@
   void InitiateConnect(WiFiService *service) {
     wifi_->ConnectTo(service);
   }
+  bool IsLinkUp() {
+    return wifi_->link_up_;
+  }
   void ReportBSS(const ::DBus::Path &bss_path,
                  const string &ssid,
                  const string &bssid,
                  int16_t signal_strength,
                  const char *mode);
+  void ReportLinkUp() {
+    wifi_->LinkEvent(IFF_LOWER_UP, IFF_LOWER_UP);
+  }
   void ReportScanDone() {
     wifi_->ScanDoneTask();
   }
@@ -181,6 +201,7 @@
 
  private:
   NiceMockControl control_interface_;
+  MockGLib glib_;
   MockManager manager_;
   WiFiRefPtr wifi_;
 
@@ -194,6 +215,8 @@
 
   scoped_ptr<MockSupplicantProcessProxy> supplicant_process_proxy_;
   scoped_ptr<MockSupplicantInterfaceProxy> supplicant_interface_proxy_;
+  MockDHCPProvider dhcp_provider_;
+  scoped_refptr<MockDHCPConfig> dhcp_config_;
 
  private:
   TestProxyFactory proxy_factory_;
@@ -329,7 +352,16 @@
         .WillOnce(Return(fake_path));
     EXPECT_CALL(supplicant_interface_proxy, SelectNetwork(fake_path));
     InitiateConnect(service);
+    EXPECT_EQ(static_cast<Service *>(service),
+              wifi()->selected_service_.get());
   }
 }
 
+TEST_F(WiFiMainTest, LinkEvent) {
+  EXPECT_FALSE(IsLinkUp());
+  EXPECT_CALL(dhcp_provider_, CreateConfig(_)).
+      WillOnce(Return(dhcp_config_));
+  ReportLinkUp();
+}
+
 }  // namespace shill