dhcp client: add RPC implementation part 2
am: dbc689e

* commit 'dbc689e3565b1b0e6fad3f2eb6cf11b02b22f39c':
  dhcp client: add RPC implementation part 2

Change-Id: I5e1ef07c5fb887c8e0c4bd47a68ef58aaf96f783
diff --git a/dbus/service_dbus_adaptor.cc b/dbus/service_dbus_adaptor.cc
index 57f6592..bffd334 100644
--- a/dbus/service_dbus_adaptor.cc
+++ b/dbus/service_dbus_adaptor.cc
@@ -36,7 +36,8 @@
     const scoped_refptr<dbus::Bus>& bus,
     ExportedObjectManager* object_manager,
     Service* service)
-    : adaptor_(this),
+    : org::chromium::dhcp_client::ServiceAdaptor(this),
+      adaptor_(this),
       object_path_(
           base::StringPrintf("%s/services/%d",
                              ManagerAdaptor::GetObjectPath().value().c_str(),
@@ -50,6 +51,12 @@
 
 ServiceDBusAdaptor::~ServiceDBusAdaptor() {}
 
+void ServiceDBusAdaptor::EmitEvent(const std::string& reason,
+                                   const brillo::VariantDictionary& configs) {
+  SendEventSignal(reason, configs);
+}
+
+
 RPCObjectIdentifier ServiceDBusAdaptor::GetRpcObjectIdentifier() {
   return object_path_;
 }
diff --git a/dbus/service_dbus_adaptor.h b/dbus/service_dbus_adaptor.h
index 80e555b..0c954b8 100644
--- a/dbus/service_dbus_adaptor.h
+++ b/dbus/service_dbus_adaptor.h
@@ -27,7 +27,8 @@
 
 class Service;
 
-class ServiceDBusAdaptor : public org::chromium::dhcp_client::ServiceInterface,
+class ServiceDBusAdaptor : public org::chromium::dhcp_client::ServiceAdaptor,
+                           public org::chromium::dhcp_client::ServiceInterface,
                            public ServiceAdaptorInterface {
  public:
   ServiceDBusAdaptor(const scoped_refptr<dbus::Bus>& bus,
@@ -37,7 +38,8 @@
 
   // Implementation of ServiceAdaptorInterface.
   RPCObjectIdentifier GetRpcObjectIdentifier() override;
-  // TODO(nywang): implement signal 'Event'
+  void EmitEvent(const std::string& reason,
+                 const brillo::VariantDictionary& configs) override;
 
  private:
   org::chromium::dhcp_client::ServiceAdaptor adaptor_;
diff --git a/dbus_bindings/org.chromium.dhcp_client.Service.dbus-xml b/dbus_bindings/org.chromium.dhcp_client.Service.dbus-xml
index e2d5101..78df6b0 100644
--- a/dbus_bindings/org.chromium.dhcp_client.Service.dbus-xml
+++ b/dbus_bindings/org.chromium.dhcp_client.Service.dbus-xml
@@ -3,6 +3,7 @@
 <node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
   <interface name="org.chromium.dhcp_client.Service">
     <signal name="Event">
+      <arg name="reason" type="s"/>
       <arg name = "configuration" type="a{sv}"/>
     </signal>
   </interface>
diff --git a/dhcp_client.gyp b/dhcp_client.gyp
index 8240ed2..7ed15aa 100644
--- a/dhcp_client.gyp
+++ b/dhcp_client.gyp
@@ -53,7 +53,6 @@
       ],
       'includes': ['../../../../platform2/common-mk/generate-dbus-adaptors.gypi'],
     },
-
     {
       'target_name': 'libdhcp_client',
       'type': 'static_library',
@@ -96,6 +95,28 @@
         'main.cc',
       ],
     },
+    # dhcp client library generated headers. Used by other daemons to
+    # interact with dhcp client.
+    {
+      'target_name': 'libdhcp_client-client-headers',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'libdhcp_client-client-dbus-proxies',
+          'variables': {
+            'dbus_service_config': 'dbus_bindings/dbus-service-config.json',
+            'proxy_output_file': 'include/dhcp_client/dbus-proxies.h',
+            'mock_output_file': 'include/dhcp_client/dbus-proxy-mocks.h',
+            'proxy_path_in_mocks': 'dhcp_client/dbus-proxies.h',
+          },
+          'sources': [
+            'dbus_bindings/org.chromium.dhcp_client.Manager.dbus-xml',
+            'dbus_bindings/org.chromium.dhcp_client.Service.dbus-xml',
+          ],
+          'includes': ['../../../../platform2/common-mk/generate-dbus-proxies.gypi'],
+        },
+      ]
+    },
   ],
   'conditions': [
     ['USE_test == 1', {
diff --git a/dhcpv4.cc b/dhcpv4.cc
index bf78157..f213074 100644
--- a/dhcpv4.cc
+++ b/dhcpv4.cc
@@ -30,10 +30,12 @@
 
 #include <base/bind.h>
 #include <base/logging.h>
+#include <brillo/variant_dictionary.h>
 
 #include "dhcp_client/dhcp_message.h"
 #include "dhcp_client/dhcp_options.h"
 #include "dhcp_client/file_io.h"
+#include "dhcp_client/service_adaptor_interface.h"
 
 using base::Bind;
 using base::Unretained;
@@ -88,9 +90,27 @@
 // TODO(nywang): find a place for the lease file.
 const char kIPV4LeaseFilePathFormat[] =
       "/tmp/lease-ipv4-%s.conf";
+
+// TODO(nywang): These constant will be moved to:
+// <dbus/dhcp_client/dbus-constants.h>
+// In this way shill can include this header and parse
+// the messages.
+
+const char kConfigurationKeyDNS[] = "DomainNameServers";
+const char kConfigurationKeyDomainName[] = "DomainName";
+const char kConfigurationKeyIPAddress[] = "IPAddress";
+const char kConfigurationKeyMTU[] = "InterfaceMTU";
+const char kConfigurationKeyRouters[] = "Routers";
+const char kConfigurationKeyVendorEncapsulatedOptions[] =
+    "VendorEncapsulatedOptions";
+const char kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
+const char kReasonBound[] = "BOUND";
+const char kReasonFail[] = "FAIL";
+const char kReasonNak[] = "NAK";
 }  // namespace
 
-DHCPV4::DHCPV4(const std::string& interface_name,
+DHCPV4::DHCPV4(ServiceAdaptorInterface* adaptor,
+               const std::string& interface_name,
                const ByteString& hardware_address,
                unsigned int interface_index,
                const std::string& network_id,
@@ -98,7 +118,8 @@
                bool arp_gateway,
                bool unicast_arp,
                EventDispatcherInterface* event_dispatcher)
-    : interface_name_(interface_name),
+    : adaptor_(adaptor),
+      interface_name_(interface_name),
       hardware_address_(hardware_address),
       interface_index_(interface_index),
       network_id_(network_id),
@@ -127,7 +148,6 @@
   server_identifier_ = 0;
   transaction_id_ = 0;
   offered_ip_address_ = 0;
-  subnet_mask_ = 0;
   client_ip_ = INADDR_ANY;
   server_ip_ = INADDR_BROADCAST;
 }
@@ -207,6 +227,7 @@
     LOG(INFO) << "Start from INIT_REBOOT state";
     if (!SendRequest()) {
       ResetStateVariables();
+      EmitEvent(kReasonFail);
       return false;
     }
     state_ = State::REBOOT;
@@ -326,7 +347,6 @@
   offered_ip_address_ = your_ip_address;
   server_identifier_ = msg.server_identifier();
   transaction_id_ = msg.transaction_id();
-  subnet_mask_ = msg.subnet_mask();
 
   if (!SendRequest()) {
     return;
@@ -395,11 +415,19 @@
   state_ = State::BOUND;
   client_ip_ = offered_ip_address_;
   server_ip_ = server_identifier_;
+  // Set the option parameters.
+  subnet_mask_ = msg.subnet_mask();
+  interface_mtu_ = msg.interface_mtu();
+  router_ = msg.router();
+  dns_server_ = msg.dns_server();
+  vendor_specific_info_ = msg.vendor_specific_info();
+  domain_name_ = msg.domain_name();
   // Write lease to persistent stotrage.
   if (!network_id_.empty()) {
     WriteLease();
   }
-  // TODO(nywang): Notify shill to configure the ip, gateway and DNS server.
+  // Send the DHCP configuration to Shill.
+  EmitEvent(kReasonBound);
   // TODO(nywang): Setup a udp socket for future unicast, so that kernel can
   // fill the ethernet header with gateway mac address for us.
 }
@@ -430,10 +458,11 @@
     LOG(INFO) << "Received DHCP NAK message with the following error message: "
               << msg.error_message();
   }
+
   // Set state variables upon receiving a valid Nak.
   ResetStateVariables();
 
-  // TODO(nywang): Notify shill the DHCP failure.
+  EmitEvent(kReasonNak);
 }
 
 bool DHCPV4::SendDiscover() {
@@ -719,6 +748,32 @@
   state_ = State::REBIND;
 }
 
+void DHCPV4::EmitEvent(const std::string& reason) {
+  brillo::VariantDictionary configs;
+  if (reason == kReasonBound) {
+    configs.emplace(kConfigurationKeyIPAddress, client_ip_);
+    configs.emplace(kConfigurationKeyMTU, interface_mtu_);
+    configs.emplace(kConfigurationKeyRouters, router_);
+    configs.emplace(kConfigurationKeyDNS, dns_server_);
+    configs.emplace(kConfigurationKeyVendorEncapsulatedOptions,
+                    vendor_specific_info_);
+    configs.emplace(kConfigurationKeyDomainName, domain_name_);
+    uint32_t subnet_cidr = MasktoCIDR(subnet_mask_);
+    configs.emplace(kConfigurationKeySubnetCIDR, subnet_cidr);
+  }
+  adaptor_->EmitEvent(reason, configs);
+}
+
+uint32_t DHCPV4::MasktoCIDR(uint32_t subnet_mask) {
+  subnet_mask = ~subnet_mask;
+  uint32_t count = 0;
+  while (subnet_mask & 1) {
+    count++;
+    subnet_mask = subnet_mask >> 1;
+  }
+  return 32 - count;
+}
+
 const std::string DHCPV4::IPtoString(uint32_t ip) {
   char buffer[INET_ADDRSTRLEN];
   ip = htonl(ip);
diff --git a/dhcpv4.h b/dhcpv4.h
index df76070..e8083f5 100644
--- a/dhcpv4.h
+++ b/dhcpv4.h
@@ -34,9 +34,12 @@
 
 namespace dhcp_client {
 
+class ServiceAdaptorInterface;
+
 class DHCPV4 : public DHCP {
  public:
-  DHCPV4(const std::string& interface_name,
+  DHCPV4(ServiceAdaptorInterface* adaptor,
+         const std::string& interface_name,
          const shill::ByteString& hardware_address,
          unsigned int interface_index,
          const std::string& network_id,
@@ -67,6 +70,8 @@
   bool ValidateOptions(const DHCPMessage& msg);
   void ResetStateVariables();
 
+  // Util functions.
+  uint32_t MasktoCIDR(uint32_t subnet_mask);
   const std::string IPtoString(uint32_t ip);
 
   void HandleOffer(const DHCPMessage& msg);
@@ -79,6 +84,11 @@
   // renewal responese from server. Therefore it is time for
   // a rebinding process.
   void RebindTask();
+
+  // Emit events through RPC adaptor
+  void EmitEvent(const std::string& reason);
+  // Serivce RPC adaptor
+  ServiceAdaptorInterface* adaptor_;
   // Interface parameters.
   std::string interface_name_;
   shill::ByteString hardware_address_;
@@ -113,6 +123,16 @@
   // Server IP address.
   // It can be either a bounded server address or an INADDR_BROADCAST constant.
   uint32_t server_ip_;
+  // Interface mtu.
+  uint16_t interface_mtu_;
+  // Aka Default Gateway.
+  std::vector<uint32_t> router_;
+  // Domain Name Servers.
+  std::vector<uint32_t> dns_server_;
+  // Vendor specific information.
+  shill::ByteString vendor_specific_info_;
+  // Domain name.
+  std::string domain_name_;
 
   // Timeout callbacks.
   base::CancelableClosure renewal_task_callback_;
diff --git a/service.cc b/service.cc
index 562980c..12080bc 100644
--- a/service.cc
+++ b/service.cc
@@ -72,7 +72,8 @@
 
   if (type_ == DHCP::SERVICE_TYPE_IPV4 ||
       type_ == DHCP::SERVICE_TYPE_BOTH) {
-    state_machine_ipv4_.reset(new DHCPV4(interface_name_,
+    state_machine_ipv4_.reset(new DHCPV4(adaptor(),
+                                         interface_name_,
                                          hardware_address_,
                                          interface_index_,
                                          network_id_,
diff --git a/service_adaptor_interface.h b/service_adaptor_interface.h
index f720541..49f7cca 100644
--- a/service_adaptor_interface.h
+++ b/service_adaptor_interface.h
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include <brillo/variant_dictionary.h>
+
 #include "dhcp_client/rpc_interface.h"
 
 namespace dhcp_client {
@@ -29,6 +31,8 @@
  public:
   virtual ~ServiceAdaptorInterface() {}
   virtual RPCObjectIdentifier GetRpcObjectIdentifier() = 0;
+  virtual void EmitEvent(const std::string& reason,
+                         const brillo::VariantDictionary& configs) = 0;
 };
 
 }  // namespace dhcp_client