shill: Don't create DHCP and Modem D-Bus proxies in signal callbacks.

dbus-c++ doesn't allow proxy creation in signal callbacks so create them in
deferred tasks instead.

BUG=chromium-os:18228
TEST=unit tests, tested on device

Change-Id: I4f85ab937aef99ef4556c5a3c16af913d8fa08fd
Reviewed-on: http://gerrit.chromium.org/gerrit/4827
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
diff --git a/dhcp_config.cc b/dhcp_config.cc
index 8e9e7ff..e9661e0 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -16,6 +16,7 @@
 #include "shill/glib.h"
 #include "shill/ip_address.h"
 #include "shill/proxy_factory.h"
+#include "shill/shill_event.h"
 
 using std::string;
 using std::vector;
@@ -44,6 +45,7 @@
 
 
 DHCPConfig::DHCPConfig(ControlInterface *control_interface,
+                       EventDispatcher *dispatcher,
                        DHCPProvider *provider,
                        const string &device_name,
                        GLib *glib)
@@ -52,6 +54,8 @@
       pid_(0),
       child_watch_tag_(0),
       root_("/"),
+      task_factory_(this),
+      dispatcher_(dispatcher),
       glib_(glib) {
   store_.RegisterConstString(flimflam::kAddressProperty,
                              &(properties().address));
@@ -85,10 +89,6 @@
   if (!pid_) {
     return false;
   }
-  if (!proxy_.get()) {
-    LOG(ERROR) << "Unable to renew IP before acquiring destination.";
-    return false;
-  }
   proxy_->Rebind(device_name());
   return true;
 }
@@ -98,17 +98,25 @@
   if (!pid_) {
     return true;
   }
-  if (!proxy_.get()) {
-    LOG(ERROR) << "Unable to release IP before acquiring destination.";
-    return false;
+  if (proxy_.get()) {
+    proxy_->Release(device_name());
   }
-  proxy_->Release(device_name());
   Stop();
   return true;
 }
 
-void DHCPConfig::InitProxy(const char *service) {
+void DHCPConfig::InitProxy(const string &service) {
+  // Defer proxy creation because dbus-c++ doesn't allow registration of new
+  // D-Bus objects in the context of a D-Bus signal handler.
   if (!proxy_.get()) {
+    dispatcher_->PostTask(
+        task_factory_.NewRunnableMethod(&DHCPConfig::InitProxyTask, service));
+  }
+}
+
+void DHCPConfig::InitProxyTask(const string &service) {
+  if (!proxy_.get()) {
+    VLOG(2) << "Init DHCP Proxy: " << device_name() << " at " << service;
     proxy_.reset(ProxyFactory::factory()->CreateDHCPProxy(service));
   }
 }