dhcp client: add RPC implementation part 2

This add the RPC implementation that allows dhcp client
to send configuations and state changes to shill.
This also adds target for client library generated headers,
which are used by other daemons to interact with dhcp client.

Bug: 25642025
TEST=compile, and test using python scripts

Change-Id: I5f4a496514828026eb9ff82728abec50c8700275
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