Update engine reinitializes the dbus connection and retries upon error.

Before this fix, update engine would keep using a failing dbus object
that was initialized once during process startup.  This causes it to
fail retrieving HTTP proxy information from Chrome in certain OOBE
scenarios, as well as when switching into / out of guest mode (see
issues 25901 and 26077). With this fix, update engine reinitializes the
dbus proxy object when dbus calls using this proxy fail with a null
error pointer.  Although these circumstances may indicate that there's
a deeper problem inside dbus, this workaround seems like a good, safe
practice, and appears to be fixing the problem.

BUG=chromium-os:25901, chromium-os:26077
TEST=Passes update_engine unittests; running the image with fluctuating
guest mode triggers reinitialization, which fixes the problem.

Change-Id: I19e81b6b718da59e22f388b264f9a723c2858a1a
Reviewed-on: https://gerrit.chromium.org/gerrit/15693
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
index 7b0bdc6..307f276 100644
--- a/chrome_browser_proxy_resolver.cc
+++ b/chrome_browser_proxy_resolver.cc
@@ -104,17 +104,24 @@
   return true;
 }
 
+void ChromeBrowserProxyResolver::Shutdown() {
+  if (!proxy_)
+    return;
+
+  GError* gerror = NULL;
+  DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
+  TEST_AND_RETURN(gbus);
+  DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
+  dbus_->DbusConnectionRemoveFilter(
+      connection,
+      &ChromeBrowserProxyResolver::StaticFilterMessage,
+      this);
+  proxy_ = NULL;
+}
+
 ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
-  if (proxy_) {
-    GError* gerror = NULL;
-    DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
-    TEST_AND_RETURN(gbus);
-    DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
-    dbus_->DbusConnectionRemoveFilter(
-        connection,
-        &ChromeBrowserProxyResolver::StaticFilterMessage,
-        this);
-  }
+  // Kill proxy object.
+  Shutdown();
   // Kill outstanding timers
   for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
        ++it) {
@@ -128,19 +135,56 @@
                                                   void* data) {
   GError* error = NULL;
   guint timeout = timeout_;
-  if (!proxy_ || !dbus_->ProxyCall(
+  if (proxy_) {
+    bool dbus_success = true;
+    bool dbus_reinit = false;
+    bool dbus_got_error;
+
+    do {
+      if (!dbus_success) {
+        // We failed with a null error, time to re-init the dbus proxy.
+        LOG(WARNING) << "attempting to reinitialize dbus proxy and retrying";
+        Shutdown();
+        if (!Init()) {
+          LOG(WARNING) << "failed to reinitialize the dbus proxy";
+          break;
+        }
+        dbus_reinit = true;
+      }
+
+      dbus_success = dbus_->ProxyCall(
           proxy_,
           kLibCrosServiceResolveNetworkProxyMethodName,
           &error,
           G_TYPE_STRING, url.c_str(),
           G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
           G_TYPE_STRING, kLibCrosProxyResolveName,
-          G_TYPE_INVALID, G_TYPE_INVALID)) {
-    LOG(WARNING) << "dbus_g_proxy_call failed: "
-                 << utils::GetAndFreeGError(&error)
-                 << " Continuing with no proxy.";
+          G_TYPE_INVALID, G_TYPE_INVALID);
+
+      dbus_got_error = false;
+
+      if (dbus_success) {
+        LOG(INFO) << "dbug_g_proxy_call succeeded!";
+      } else {
+        if (error) {
+          // Register the fact that we did receive an error, as it is nullified
+          // on the next line.
+          dbus_got_error = true;
+          LOG(WARNING) << "dbus_g_proxy_call failed: "
+                       << utils::GetAndFreeGError(&error)
+                       << " Continuing with no proxy.";
+        } else {
+          LOG(WARNING) << "dbug_g_proxy_call failed with no error string, "
+                          "continuing with no proxy.";
+        }
+        timeout = 0;
+      }
+    } while (!(dbus_success || dbus_got_error || dbus_reinit));
+  } else {
+    LOG(WARNING) << "dbug proxy object missing, continuing with no proxy.";
     timeout = 0;
   }
+
   callbacks_.insert(make_pair(url, make_pair(callback, data)));
   Closure* closure = NewCallback(this,
                                  &ChromeBrowserProxyResolver::HandleTimeout,
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
index 429c772..61f5982 100644
--- a/chrome_browser_proxy_resolver.h
+++ b/chrome_browser_proxy_resolver.h
@@ -72,6 +72,9 @@
                       bool delete_timer,
                       std::pair<ProxiesResolvedFn, void*>* callback);
 
+  // Shutdown the dbus proxy object.
+  void Shutdown();
+
   DbusGlibInterface* dbus_;
   DBusGProxy* proxy_;
   DBusGProxy* peer_proxy_;