AU: Full proxy support

This CL Introduces a new proxy resolver that queries Chrome browser
for all URLs. This means the updater now works with Proxy
AutoConfiguration (PAC) files and automatic proxy detection.

BUG=chromium-os:9478
TEST=performed update, unittests

Review URL: http://codereview.chromium.org/6594025

Change-Id: I2c169aec06109e3eabc4011e0b536685dfd13f79
diff --git a/SConstruct b/SConstruct
index f75b256..dea012d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -238,6 +238,7 @@
                    buffered_file_writer.cc
                    bzip.cc
                    bzip_extent_writer.cc
+                   chrome_browser_proxy_resolver.cc
                    chrome_proxy_resolver.cc
                    cycle_breaker.cc
                    dbus_service.cc
@@ -285,6 +286,7 @@
                             action_processor_unittest.cc
                             buffered_file_writer_unittest.cc
                             bzip_extent_writer_unittest.cc
+                            chrome_browser_proxy_resolver_unittest.cc
                             chrome_proxy_resolver_unittest.cc
                             cycle_breaker_unittest.cc
                             decompressing_file_writer_unittest.cc
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 534fade..949e5bb 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -28,6 +28,7 @@
     <allow send_destination="org.chromium.UpdateEngine"
            send_interface="org.chromium.UpdateEngineInterface"
            send_member="SetTrack"/>
+    <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
   </policy>
   <policy context="default">
     <deny send_destination="org.chromium.UpdateEngine" />
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
new file mode 100644
index 0000000..a481f4f
--- /dev/null
+++ b/chrome_browser_proxy_resolver.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/chrome_browser_proxy_resolver.h"
+
+#include <map>
+#include <string>
+
+#include <base/string_tokenizer.h>
+#include <base/string_util.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <google/protobuf/stubs/common.h>
+
+#include "update_engine/dbus_constants.h"
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+using google::protobuf::Closure;
+using google::protobuf::NewCallback;
+using std::deque;
+using std::make_pair;
+using std::multimap;
+using std::pair;
+using std::string;
+
+#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
+#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE                 \
+  "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
+const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
+const char kLibCrosServiceResolveNetworkProxyMethodName[] =
+    "ResolveNetworkProxy";
+const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
+const char kLibCrosProxyResolveSignalInterface[] =
+    LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
+const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
+    "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
+    "path='/', "
+    "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
+#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
+#undef LIB_CROS_PROXY_RESOLVE_NAME
+
+namespace {
+const int kTimeout = 5;  // seconds
+
+DBusGProxy* GetProxy(DbusGlibInterface* dbus) {
+  GError* error = NULL;
+
+  DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error);
+  if (!bus) {
+    LOG(ERROR) << "Failed to get bus";
+    return NULL;
+  }
+  DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus,
+                                                 kLibCrosServiceName,
+                                                 kLibCrosServicePath,
+                                                 kLibCrosServiceInterface,
+                                                 &error);
+  if (!proxy) {
+    LOG(ERROR) << "Error getting dbus proxy for "
+               << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error);
+    return NULL;
+  }
+  return proxy;
+}
+
+}  // namespace {}
+
+ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
+    : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}
+
+bool ChromeBrowserProxyResolver::Init() {
+  // Set up signal handler. Code lifted from libcros
+  if (proxy_) {
+    // Already initialized
+    return true;
+  }
+  GError* gerror = NULL;
+  DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
+  TEST_AND_RETURN_FALSE(gbus);
+  DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
+  TEST_AND_RETURN_FALSE(connection);
+  DBusError error;
+  dbus_error_init(&error);
+  dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error);
+  TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error));
+  TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
+      connection,
+      &ChromeBrowserProxyResolver::StaticFilterMessage,
+      this,
+      NULL));
+
+  proxy_ = GetProxy(dbus_);
+  if (!proxy_) {
+    dbus_->DbusConnectionRemoveFilter(
+        connection,
+        &ChromeBrowserProxyResolver::StaticFilterMessage,
+        this);
+  }
+  TEST_AND_RETURN_FALSE(proxy_);  // For the error log
+  return true;
+}
+
+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 outstanding timers
+  for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
+       ++it) {
+    g_source_destroy(it->second);
+    it->second = NULL;
+  }
+}
+
+bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
+                                                  ProxiesResolvedFn callback,
+                                                  void* data) {
+  GError* error = NULL;
+  TEST_AND_RETURN_FALSE(proxy_);
+  if (!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(ERROR) << "dbus_g_proxy_call failed: "
+               << utils::GetGErrorMessage(error);
+    return false;
+  }
+  callbacks_.insert(make_pair(url, make_pair(callback, data)));
+  Closure* closure = NewCallback(this,
+                                 &ChromeBrowserProxyResolver::HandleTimeout,
+                                 url);
+  GSource* timer = g_timeout_source_new_seconds(timeout_);
+  g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
+  g_source_attach(timer, NULL);
+  timers_.insert(make_pair(url, timer));
+  return true;
+}
+
+DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
+    DBusConnection* connection,
+    DBusMessage* message) {
+  // Code lifted from libcros.
+  if (!dbus_->DbusMessageIsSignal(message,
+                                  kLibCrosProxyResolveSignalInterface,
+                                  kLibCrosProxyResolveName)) {
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+  // Get args
+  char* source_url = NULL;
+  char* proxy_list = NULL;
+  char* error = NULL;
+  DBusError arg_error;
+  dbus_error_init(&arg_error);
+  if (!dbus_->DbusMessageGetArgs(message, &arg_error,
+                                 DBUS_TYPE_STRING, &source_url,
+                                 DBUS_TYPE_STRING, &proxy_list,
+                                 DBUS_TYPE_STRING, &error,
+                                 DBUS_TYPE_INVALID)) {
+    LOG(ERROR) << "Error reading dbus signal.";
+    return DBUS_HANDLER_RESULT_HANDLED;
+  }
+  if (!source_url || !proxy_list) {
+    LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
+    return DBUS_HANDLER_RESULT_HANDLED;
+  }
+  HandleReply(source_url, proxy_list);
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+bool ChromeBrowserProxyResolver::DeleteUrlState(
+    const string& source_url,
+    bool delete_timer,
+    pair<ProxiesResolvedFn, void*>* callback) {
+  {
+    CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
+    TEST_AND_RETURN_FALSE(it != callbacks_.end());
+    TEST_AND_RETURN_FALSE(it->first == source_url);
+    if (callback)
+      *callback = it->second;
+    callbacks_.erase(it);
+  }
+  {
+    TimeoutsMap::iterator it = timers_.lower_bound(source_url);
+    TEST_AND_RETURN_FALSE(it != timers_.end());
+    TEST_AND_RETURN_FALSE(it->first == source_url);
+    if (delete_timer)
+      g_source_destroy(it->second);
+    timers_.erase(it);
+  }
+  return true;
+}
+
+void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
+                                             const string& proxy_list) {
+  pair<ProxiesResolvedFn, void*> callback;
+  TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
+  (*callback.first)(ParseProxyString(proxy_list), callback.second);
+}
+
+void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
+  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
+  pair<ProxiesResolvedFn, void*> callback;
+  TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
+  deque<string> proxies;
+  proxies.push_back(kNoProxy);
+  (*callback.first)(proxies, callback.second);
+}
+
+deque<string> ChromeBrowserProxyResolver::ParseProxyString(
+    const string& input) {
+  deque<string> ret;
+  // Some of this code taken from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+  StringTokenizer entry_tok(input, ";");
+  while (entry_tok.GetNext()) {
+    string token = entry_tok.token();
+    TrimWhitespaceASCII(token, TRIM_ALL, &token);
+
+    // Start by finding the first space (if any).
+    std::string::iterator space;
+    for (space = token.begin(); space != token.end(); ++space) {
+      if (IsAsciiWhitespace(*space)) {
+        break;
+      }
+    }
+
+    string scheme = string(token.begin(), space);
+    StringToLowerASCII(&scheme);
+    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+    if (scheme == "socks")
+      scheme += "4";
+    else if (scheme == "proxy")
+      scheme = "http";
+    else if (scheme != "https" &&
+             scheme != "socks4" &&
+             scheme != "socks5" &&
+             scheme != "direct")
+      continue;  // Invalid proxy scheme
+
+    string host_and_port = string(space, token.end());
+    TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
+    if (scheme != "direct" && host_and_port.empty())
+      continue;  // Must supply host/port when non-direct proxy used.
+    ret.push_back(scheme + "://" + host_and_port);
+  }
+  if (ret.empty() || *ret.rbegin() != kNoProxy)
+    ret.push_back(kNoProxy);
+  return ret;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
new file mode 100644
index 0000000..429c772
--- /dev/null
+++ b/chrome_browser_proxy_resolver.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H__
+
+#include <map>
+#include <string>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/dbus_interface.h"
+#include "update_engine/proxy_resolver.h"
+
+namespace chromeos_update_engine {
+
+extern const char kLibCrosServiceName[];
+extern const char kLibCrosServicePath[];
+extern const char kLibCrosServiceInterface[];
+extern const char kLibCrosServiceResolveNetworkProxyMethodName[];
+extern const char kLibCrosProxyResolveName[];
+extern const char kLibCrosProxyResolveSignalInterface[];
+extern const char kLibCrosProxyResolveSignalFilter[];
+
+class ChromeBrowserProxyResolver : public ProxyResolver {
+ public:
+  explicit ChromeBrowserProxyResolver(DbusGlibInterface* dbus);
+  virtual ~ChromeBrowserProxyResolver();
+  bool Init();
+
+  virtual bool GetProxiesForUrl(const std::string& url,
+                                ProxiesResolvedFn callback,
+                                void* data);
+  void set_timeout(int seconds) { timeout_ = seconds; }
+
+  // Public for test
+  static DBusHandlerResult StaticFilterMessage(
+      DBusConnection* connection,
+      DBusMessage* message,
+      void* data) {
+    return reinterpret_cast<ChromeBrowserProxyResolver*>(data)->FilterMessage(
+        connection, message);
+  }
+
+ private:
+  FRIEND_TEST(ChromeBrowserProxyResolverTest, ParseTest);
+  FRIEND_TEST(ChromeBrowserProxyResolverTest, SuccessTest);
+  typedef std::multimap<std::string, std::pair<ProxiesResolvedFn, void*> >
+    CallbacksMap;
+  typedef std::multimap<std::string, GSource*> TimeoutsMap;
+
+  // Handle a reply from Chrome:
+  void HandleReply(const std::string& source_url,
+                   const std::string& proxy_list);
+  DBusHandlerResult FilterMessage(
+      DBusConnection* connection,
+      DBusMessage* message);
+  // Handle no reply:
+  void HandleTimeout(std::string source_url);
+  
+  // Parses a string-encoded list of proxies and returns a deque
+  // of individual proxies. The last one will always be kNoProxy.
+  static std::deque<std::string> ParseProxyString(const std::string& input);
+  
+  // Deletes internal state for the first instance of url in the state.
+  // If delete_timer is set, calls g_source_destroy on the timer source.
+  // Returns the callback in an out parameter. Returns true on success.
+  bool DeleteUrlState(const std::string& url,
+                      bool delete_timer,
+                      std::pair<ProxiesResolvedFn, void*>* callback);
+
+  DbusGlibInterface* dbus_;
+  DBusGProxy* proxy_;
+  DBusGProxy* peer_proxy_;
+  int timeout_;
+  TimeoutsMap timers_;
+  CallbacksMap callbacks_;
+  DISALLOW_COPY_AND_ASSIGN(ChromeBrowserProxyResolver);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H__
diff --git a/chrome_browser_proxy_resolver_unittest.cc b/chrome_browser_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..09a8be7
--- /dev/null
+++ b/chrome_browser_proxy_resolver_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <deque>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/mock_dbus_interface.h"
+
+using std::deque;
+using std::string;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace chromeos_update_engine {
+
+class ChromeBrowserProxyResolverTest : public ::testing::Test { };
+
+TEST(ChromeBrowserProxyResolverTest, ParseTest) {
+  // Test ideas from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
+  const char* inputs[] = {
+    "PROXY foopy:10",
+    " DIRECT",  // leading space.
+    "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+    "proxy foopy1 ; SOCKS foopy2",
+    "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+    "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+    "PROXY-foopy:10",
+    "PROXY",
+    "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+    "HTTP foopy1; SOCKS5 foopy2"
+  };
+  deque<string> outputs[arraysize(inputs)];
+  outputs[0].push_back("http://foopy:10");
+  outputs[0].push_back(kNoProxy);
+  outputs[1].push_back(kNoProxy);
+  outputs[2].push_back("http://foopy1");
+  outputs[2].push_back("http://foopy2");
+  outputs[2].push_back(kNoProxy);
+  outputs[3].push_back("http://foopy1");
+  outputs[3].push_back("socks4://foopy2");
+  outputs[3].push_back(kNoProxy);
+  outputs[4].push_back(kNoProxy);
+  outputs[4].push_back("http://foopy1");
+  outputs[4].push_back(kNoProxy);
+  outputs[4].push_back("socks5://foopy2");
+  outputs[4].push_back(kNoProxy);
+  outputs[5].push_back(kNoProxy);
+  outputs[5].push_back("http://foopy1:80");
+  outputs[5].push_back(kNoProxy);
+  outputs[5].push_back(kNoProxy);
+  outputs[6].push_back(kNoProxy);
+  outputs[7].push_back(kNoProxy);
+  outputs[8].push_back("http://foopy1");
+  outputs[8].push_back("socks5://foopy2");
+  outputs[8].push_back(kNoProxy);
+  outputs[9].push_back("socks5://foopy2");
+  outputs[9].push_back(kNoProxy);
+
+  for (size_t i = 0; i < arraysize(inputs); i++) {
+    deque<string> results =
+        ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
+    deque<string>& expected = outputs[i];
+    EXPECT_EQ(results.size(), expected.size()) << "i = " << i;
+    if (expected.size() != results.size())
+      continue;
+    for (size_t j = 0; j < expected.size(); j++) {
+      EXPECT_EQ(expected[j], results[j]) << "i = " << i;
+    }
+  }
+}
+
+namespace {
+void DbusInterfaceTestResolved(const std::deque<std::string>& proxies,
+                               void* data) {
+  EXPECT_EQ(2, proxies.size());
+  EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+  EXPECT_EQ(kNoProxy, proxies[1]);
+  g_main_loop_quit(reinterpret_cast<GMainLoop*>(data));
+}
+void DbusInterfaceTestResolvedNoReply(const std::deque<std::string>& proxies,
+                                      void* data) {
+  EXPECT_EQ(1, proxies.size());
+  EXPECT_EQ(kNoProxy, proxies[0]);
+  g_main_loop_quit(reinterpret_cast<GMainLoop*>(data));
+}
+struct SendReplyArgs {
+  DBusConnection* connection;
+  DBusMessage* message;
+  ChromeBrowserProxyResolver* resolver;
+};
+gboolean SendReply(gpointer data) {
+  LOG(INFO) << "Calling SendReply";
+  SendReplyArgs* args = reinterpret_cast<SendReplyArgs*>(data);
+  ChromeBrowserProxyResolver::StaticFilterMessage(
+      args->connection,
+      args->message,
+      args->resolver);
+  return FALSE;  // Don't keep calling this function
+}
+
+// chrome_replies should be set to whether or not we fake a reply from
+// chrome. If there's no reply, the resolver should time out.
+void RunTest(bool chrome_replies) {
+  long number = 1;
+  DBusGConnection* kMockSystemGBus =
+      reinterpret_cast<DBusGConnection*>(number++);
+  DBusConnection* kMockSystemBus =
+      reinterpret_cast<DBusConnection*>(number++);
+  DBusGProxy* kMockDbusProxy =
+      reinterpret_cast<DBusGProxy*>(number++);
+  DBusMessage* kMockDbusMessage =
+      reinterpret_cast<DBusMessage*>(number++);
+
+  const char kUrl[] = "http://example.com/blah";
+
+  MockDbusGlib dbus_iface;
+  
+  EXPECT_CALL(dbus_iface, BusGet(_, _))
+      .Times(3)
+      .WillRepeatedly(Return(kMockSystemGBus));
+  EXPECT_CALL(dbus_iface,
+              ConnectionGetConnection(kMockSystemGBus))
+      .Times(2)
+      .WillRepeatedly(Return(kMockSystemBus));
+  EXPECT_CALL(dbus_iface, DbusBusAddMatch(kMockSystemBus, _, _));
+  EXPECT_CALL(dbus_iface,
+              DbusConnectionAddFilter(kMockSystemBus, _, _, _))
+      .WillOnce(Return(1));
+  EXPECT_CALL(dbus_iface,
+              ProxyNewForNameOwner(kMockSystemGBus,
+                                   StrEq(kLibCrosServiceName),
+                                   StrEq(kLibCrosServicePath),
+                                   StrEq(kLibCrosServiceInterface),
+                                   _))
+      .WillOnce(Return(kMockDbusProxy));
+  EXPECT_CALL(dbus_iface, ProxyCall(
+      kMockDbusProxy,
+      StrEq(kLibCrosServiceResolveNetworkProxyMethodName),
+      _,
+      G_TYPE_STRING, StrEq(kUrl),
+      G_TYPE_STRING, StrEq(kLibCrosProxyResolveSignalInterface),
+      G_TYPE_STRING, StrEq(kLibCrosProxyResolveName),
+      G_TYPE_INVALID))
+      .WillOnce(Return(TRUE));
+  EXPECT_CALL(dbus_iface,
+              DbusConnectionRemoveFilter(kMockSystemBus, _, _));
+  if (chrome_replies) {
+    EXPECT_CALL(dbus_iface,
+                DbusMessageIsSignal(kMockDbusMessage,
+                                    kLibCrosProxyResolveSignalInterface,
+                                    kLibCrosProxyResolveName))
+        .WillOnce(Return(1));
+    EXPECT_CALL(dbus_iface,
+                DbusMessageGetArgs(kMockDbusMessage, _,
+                                   DBUS_TYPE_STRING, _,
+                                   DBUS_TYPE_STRING, _,
+                                   DBUS_TYPE_STRING, _,
+                                   DBUS_TYPE_INVALID))
+        .WillOnce(DoAll(SetArgumentPointee<3>(strdup(kUrl)),
+                        SetArgumentPointee<5>(
+                            strdup("SOCKS5 192.168.52.83:5555;DIRECT")),
+                        Return(TRUE)));
+  }
+
+  GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  ChromeBrowserProxyResolver resolver(&dbus_iface);
+  EXPECT_TRUE(resolver.Init());
+  resolver.set_timeout(1);
+  SendReplyArgs args = {
+    kMockSystemBus,
+    kMockDbusMessage,
+    &resolver
+  };
+  if (chrome_replies)
+    g_idle_add(SendReply, &args);
+  EXPECT_TRUE(resolver.GetProxiesForUrl(kUrl,
+                                        chrome_replies ?
+                                        &DbusInterfaceTestResolved :
+                                        &DbusInterfaceTestResolvedNoReply,
+                                        loop));
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+}
+}  // namespace {}
+
+TEST(ChromeBrowserProxyResolverTest, SuccessTest) {
+  RunTest(true);
+}
+
+TEST(ChromeBrowserProxyResolverTest, NoReplyTest) {
+  RunTest(false);
+}
+
+TEST(ChromeBrowserProxyResolverTest, NoChromeTest) {
+  long number = 1;
+  DBusGConnection* kMockSystemGBus =
+      reinterpret_cast<DBusGConnection*>(number++);
+  DBusConnection* kMockSystemBus =
+      reinterpret_cast<DBusConnection*>(number++);
+
+  const char kUrl[] = "http://example.com/blah";
+
+  MockDbusGlib dbus_iface;
+  
+  EXPECT_CALL(dbus_iface, BusGet(_, _))
+      .Times(2)
+      .WillRepeatedly(Return(kMockSystemGBus));
+  EXPECT_CALL(dbus_iface,
+              ConnectionGetConnection(kMockSystemGBus))
+      .Times(1)
+      .WillOnce(Return(kMockSystemBus));
+  EXPECT_CALL(dbus_iface, DbusBusAddMatch(kMockSystemBus, _, _));
+  EXPECT_CALL(dbus_iface,
+              DbusConnectionAddFilter(kMockSystemBus, _, _, _))
+      .WillOnce(Return(1));
+  EXPECT_CALL(dbus_iface,
+              ProxyNewForNameOwner(kMockSystemGBus,
+                                   StrEq(kLibCrosServiceName),
+                                   StrEq(kLibCrosServicePath),
+                                   StrEq(kLibCrosServiceInterface),
+                                   _))
+      .WillOnce(Return(static_cast<DBusGProxy*>(NULL)));
+  EXPECT_CALL(dbus_iface,
+              DbusConnectionRemoveFilter(kMockSystemBus, _, _));
+  ChromeBrowserProxyResolver resolver(&dbus_iface);
+  EXPECT_FALSE(resolver.Init());
+  EXPECT_FALSE(resolver.GetProxiesForUrl(kUrl, NULL, NULL));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_constants.h b/dbus_constants.h
index bd5b551..e4173f0 100644
--- a/dbus_constants.h
+++ b/dbus_constants.h
@@ -12,7 +12,6 @@
     "/org/chromium/UpdateEngine";
 static const char* const kUpdateEngineServiceInterface =
     "org.chromium.UpdateEngineInterface";
-
 }  // namespace chromeos_update_engine
 
 #endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_CONSTANTS_H__
diff --git a/dbus_interface.h b/dbus_interface.h
index de09fff..ec266b5 100644
--- a/dbus_interface.h
+++ b/dbus_interface.h
@@ -9,6 +9,7 @@
 
 #include <base/logging.h>
 #include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
 namespace chromeos_update_engine {
 
@@ -25,7 +26,7 @@
   virtual void ProxyUnref(DBusGProxy* proxy) = 0;
 
   // wraps dbus_g_bus_get
-  virtual DBusGConnection*  BusGet(DBusBusType type, GError** error) = 0;
+  virtual DBusGConnection* BusGet(DBusBusType type, GError** error) = 0;
 
   // wraps dbus_g_proxy_call
   virtual gboolean ProxyCall(DBusGProxy* proxy,
@@ -44,6 +45,38 @@
                              GType var_arg4, gchar** var_arg5,
                              GType var_arg6, GArray** var_arg7,
                              GType var_arg8) = 0;
+
+  virtual gboolean ProxyCall(DBusGProxy* proxy,
+                             const char* method,
+                             GError** error,
+                             GType var_arg1, const char* var_arg2,
+                             GType var_arg3, const char* var_arg4,
+                             GType var_arg5, const char* var_arg6,
+                             GType var_arg7, GType var_arg8) = 0;
+
+  virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) = 0;
+
+  virtual void DbusBusAddMatch(DBusConnection* connection,
+                               const char* rule,
+                               DBusError* error) = 0;
+
+  virtual dbus_bool_t DbusConnectionAddFilter(
+      DBusConnection* connection,
+      DBusHandleMessageFunction function,
+      void* user_data,
+      DBusFreeFunction free_data_function) = 0;
+  virtual void DbusConnectionRemoveFilter(DBusConnection* connection,
+                                          DBusHandleMessageFunction function,
+                                          void* user_data) = 0;
+  virtual dbus_bool_t DbusMessageIsSignal(DBusMessage* message,
+                                          const char* interface,
+                                          const char* signal_name) = 0;
+  virtual dbus_bool_t DbusMessageGetArgs(DBusMessage* message,
+                                         DBusError* error,
+                                         GType var_arg1, char** var_arg2,
+                                         GType var_arg3, char** var_arg4,
+                                         GType var_arg5, char** var_arg6,
+                                         GType var_arg7) = 0;
 };
 
 class ConcreteDbusGlib : public DbusGlibInterface {
@@ -63,7 +96,7 @@
     g_object_unref(proxy);
   }
 
-  virtual DBusGConnection*  BusGet(DBusBusType type, GError** error) {
+  virtual DBusGConnection* BusGet(DBusBusType type, GError** error) {
     return dbus_g_bus_get(type, error);
   }
   
@@ -90,6 +123,62 @@
         proxy, method, error, var_arg1, var_arg2, var_arg3,
         var_arg4, var_arg5, var_arg6, var_arg7, var_arg8);
   }
+
+  virtual gboolean ProxyCall(DBusGProxy* proxy,
+                             const char* method,
+                             GError** error,
+                             GType var_arg1, const char* var_arg2,
+                             GType var_arg3, const char* var_arg4,
+                             GType var_arg5, const char* var_arg6,
+                             GType var_arg7, GType var_arg8) {
+    return dbus_g_proxy_call(
+        proxy, method, error, var_arg1, var_arg2, var_arg3,
+        var_arg4, var_arg5, var_arg6, var_arg7, var_arg8);
+  }
+
+
+  virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) {
+    return dbus_g_connection_get_connection(gbus);
+  }
+
+  virtual void DbusBusAddMatch(DBusConnection* connection,
+                               const char* rule,
+                               DBusError* error) {
+    dbus_bus_add_match(connection, rule, error);
+  }
+
+  virtual dbus_bool_t DbusConnectionAddFilter(
+      DBusConnection* connection,
+      DBusHandleMessageFunction function,
+      void* user_data,
+      DBusFreeFunction free_data_function) {
+    return dbus_connection_add_filter(connection,
+                                      function,
+                                      user_data,
+                                      free_data_function);
+  }
+  virtual void DbusConnectionRemoveFilter(DBusConnection* connection,
+                                          DBusHandleMessageFunction function,
+                                          void* user_data) {
+    dbus_connection_remove_filter(connection, function, user_data);
+  }
+  dbus_bool_t DbusMessageIsSignal(DBusMessage* message,
+                                  const char* interface,
+                                  const char* signal_name) {
+    return dbus_message_is_signal(message, interface, signal_name);
+  }
+  virtual dbus_bool_t DbusMessageGetArgs(DBusMessage* message,
+                                         DBusError* error,
+                                         GType var_arg1, char** var_arg2,
+                                         GType var_arg3, char** var_arg4,
+                                         GType var_arg5, char** var_arg6,
+                                         GType var_arg7) {
+    return dbus_message_get_args(message, error,
+                                 var_arg1, var_arg2,
+                                 var_arg3, var_arg4,
+                                 var_arg5, var_arg6,
+                                 var_arg7);
+  }
 };
 
 }  // namespace chromeos_update_engine
diff --git a/main.cc b/main.cc
index 9dae81a..48ea9f1 100755
--- a/main.cc
+++ b/main.cc
@@ -74,7 +74,6 @@
                                     DBUS_SERVICE_DBUS,
                                     DBUS_PATH_DBUS,
                                     DBUS_INTERFACE_DBUS);
-
   guint32 request_name_ret;
   if (!org_freedesktop_DBus_request_name(proxy,
                                          kUpdateEngineServiceName,
@@ -146,6 +145,7 @@
                        logging::DONT_LOCK_LOG_FILE,
                        logging::APPEND_TO_OLD_LOG_FILE);
 }
+
 }  // namespace {}
 }  // namespace chromeos_update_engine
 
diff --git a/mock_dbus_interface.h b/mock_dbus_interface.h
index c817109..781b3f3 100644
--- a/mock_dbus_interface.h
+++ b/mock_dbus_interface.h
@@ -37,19 +37,70 @@
                                     GType var_arg3,
                                     GType var_arg4, gchar** var_arg5,
                                     GType var_arg6, GArray** var_arg7));
+  MOCK_METHOD10(ProxyCall, gboolean(DBusGProxy* proxy,
+                                    const char* method,
+                                    GError** error,
+                                    GType var_arg1, const char* var_arg2,
+                                    GType var_arg3, const char* var_arg4,
+                                    GType var_arg5, const char* var_arg6,
+                                    GType var_arg7));
+
+  MOCK_METHOD1(ConnectionGetConnection, DBusConnection*(DBusGConnection* gbus));
+
+  MOCK_METHOD3(DbusBusAddMatch, void(DBusConnection* connection,
+                                     const char* rule,
+                                     DBusError* error));
+
+  MOCK_METHOD4(DbusConnectionAddFilter, dbus_bool_t(
+      DBusConnection* connection,
+      DBusHandleMessageFunction function,
+      void* user_data,
+      DBusFreeFunction free_data_function));
+
+  MOCK_METHOD3(DbusConnectionRemoveFilter, void(
+      DBusConnection* connection,
+      DBusHandleMessageFunction function,
+      void* user_data));
+
+  MOCK_METHOD3(DbusMessageIsSignal, dbus_bool_t(DBusMessage* message,
+                                                const char* interface,
+                                                const char* signal_name));
+
+  MOCK_METHOD9(DbusMessageGetArgs, dbus_bool_t(
+      DBusMessage* message,
+      DBusError* error,
+      GType var_arg1, char** var_arg2,
+      GType var_arg3, char** var_arg4,
+      GType var_arg5, char** var_arg6,
+      GType var_arg7));
 
   // Since gmock only supports mocking functions up to 10 args, we
   // take the 11-arg function we'd like to mock, drop the last arg
   // and call the 10-arg version. Dropping the last arg isn't ideal,
   // but this is a lot better than nothing.
   gboolean ProxyCall(DBusGProxy* proxy,
-                       const char* method,
-                       GError** error,
-                       GType var_arg1, const char* var_arg2,
-                       GType var_arg3,
-                       GType var_arg4, gchar** var_arg5,
-                       GType var_arg6, GArray** var_arg7,
-                       GType var_arg8) {
+                     const char* method,
+                     GError** error,
+                     GType var_arg1, const char* var_arg2,
+                     GType var_arg3,
+                     GType var_arg4, gchar** var_arg5,
+                     GType var_arg6, GArray** var_arg7,
+                     GType var_arg8) {
+    return ProxyCall(proxy,
+                     method,
+                     error,
+                     var_arg1, var_arg2,
+                     var_arg3,
+                     var_arg4, var_arg5,
+                     var_arg6, var_arg7);
+  }
+  gboolean ProxyCall(DBusGProxy* proxy,
+                     const char* method,
+                     GError** error,
+                     GType var_arg1, const char* var_arg2,
+                     GType var_arg3, const char* var_arg4,
+                     GType var_arg5, const char* var_arg6,
+                     GType var_arg7, GType var_arg8) {
     return ProxyCall(proxy,
                      method,
                      error,
diff --git a/update_attempter.cc b/update_attempter.cc
index 949434e..e49a551 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -125,6 +125,7 @@
 void UpdateAttempter::Update(const std::string& app_version,
                              const std::string& omaha_url,
                              bool obey_proxies) {
+  chrome_proxy_resolver_.Init();
   if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
     LOG(INFO) << "Not updating b/c we already updated and we're waiting for "
               << "reboot";
diff --git a/update_attempter.h b/update_attempter.h
index 1709a3a..0afe7f6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -16,7 +16,7 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/action_processor.h"
-#include "update_engine/chrome_proxy_resolver.h"
+#include "update_engine/chrome_browser_proxy_resolver.h"
 #include "update_engine/download_action.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/omaha_response_handler_action.h"
@@ -235,7 +235,7 @@
 
   // Our two proxy resolvers
   DirectProxyResolver direct_proxy_resolver_;
-  ChromeProxyResolver chrome_proxy_resolver_;
+  ChromeBrowserProxyResolver chrome_proxy_resolver_;
 
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
 };
diff --git a/update_attempter_mock.h b/update_attempter_mock.h
index 908faeb..16c0e11 100644
--- a/update_attempter_mock.h
+++ b/update_attempter_mock.h
@@ -14,12 +14,12 @@
 
 class UpdateAttempterMock : public UpdateAttempter {
  public:
-  UpdateAttempterMock() : UpdateAttempter(NULL, NULL, &dbus_) {}
+  explicit UpdateAttempterMock(MockDbusGlib* dbus)
+      : UpdateAttempter(NULL, NULL, dbus) {}
 
   MOCK_METHOD3(Update, void(const std::string& app_version,
                             const std::string& omaha_url,
                             bool obey_proxies));
-  MockDbusGlib dbus_;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 2f60609..76068fd 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -33,13 +33,13 @@
 // methods.
 class UpdateAttempterUnderTest : public UpdateAttempter {
  public:
-  UpdateAttempterUnderTest()
-      : UpdateAttempter(NULL, NULL, &dbus_) {}
-  MockDbusGlib dbus_;
+  explicit UpdateAttempterUnderTest(MockDbusGlib* dbus)
+      : UpdateAttempter(NULL, NULL, dbus) {}
 };
 
 class UpdateAttempterTest : public ::testing::Test {
  protected:
+  UpdateAttempterTest() : attempter_(&dbus_) {}
   virtual void SetUp() {
     EXPECT_EQ(NULL, attempter_.dbus_service_);
     EXPECT_EQ(NULL, attempter_.prefs_);
@@ -60,6 +60,7 @@
     attempter_.prefs_ = &prefs_;
   }
 
+  MockDbusGlib dbus_;
   UpdateAttempterUnderTest attempter_;
   ActionProcessorMock* processor_;
   NiceMock<PrefsMock> prefs_;
@@ -110,7 +111,8 @@
   extern const char* kUpdateCompletedMarker;
   const FilePath kMarker(kUpdateCompletedMarker);
   EXPECT_EQ(0, file_util::WriteFile(kMarker, "", 0));
-  UpdateAttempterUnderTest attempter;
+  MockDbusGlib dbus;
+  UpdateAttempterUnderTest attempter(&dbus);
   EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter.status());
   EXPECT_TRUE(file_util::Delete(kMarker, false));
 }
diff --git a/update_check_scheduler_unittest.cc b/update_check_scheduler_unittest.cc
index c267108..c1dd330 100644
--- a/update_check_scheduler_unittest.cc
+++ b/update_check_scheduler_unittest.cc
@@ -40,7 +40,7 @@
 
 class UpdateCheckSchedulerTest : public ::testing::Test {
  public:
-  UpdateCheckSchedulerTest() : scheduler_(&attempter_) {}
+  UpdateCheckSchedulerTest() : scheduler_(&attempter_), attempter_(&dbus_) {}
 
  protected:
   virtual void SetUp() {
@@ -65,6 +65,7 @@
   }
 
   UpdateCheckSchedulerUnderTest scheduler_;
+  MockDbusGlib dbus_;
   UpdateAttempterMock attempter_;
   MockFunction<gboolean(gpointer data)> source_callback_;
   GMainLoop* loop_;