update_engine: Use org.chromium.NetworkProxyService.

Make update_engine call Chrome's new
org.chromium.NetworkProxyService D-Bus service to resolve
network proxies instead of using
org.chromium.LibCrosService. The new service supports
asynchronous replies instead of responding via D-Bus
signals.

BUG=chromium:446115,chromium:703217
TEST=unit tests pass; also added debug logging and verified
     that chrome's proxy settings are used

(cherry picked from commit 941cf235c5e56eddc6e4f2de2f38bee032a4dead)
Cherry-pick updated to resolve conflicts with existing code in AOSP.

Change-Id: I8c0704482e9988fe9ed14d32797b3a5b8da3d46a
Reviewed-on: https://chromium-review.googlesource.com/497491
Commit-Ready: Dan Erat <derat@chromium.org>
Tested-by: Dan Erat <derat@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
index 09365c1..12a8328 100644
--- a/chrome_browser_proxy_resolver.cc
+++ b/chrome_browser_proxy_resolver.cc
@@ -16,141 +16,37 @@
 
 #include "update_engine/chrome_browser_proxy_resolver.h"
 
-#include <deque>
-#include <string>
+#include <utility>
 
 #include <base/bind.h>
+#include <base/memory/ptr_util.h>
 #include <base/strings/string_tokenizer.h>
 #include <base/strings/string_util.h>
 
-#include "update_engine/common/utils.h"
+#include "network_proxy/dbus-proxies.h"
 
 namespace chromeos_update_engine {
 
 using base::StringTokenizer;
-using base::TimeDelta;
-using brillo::MessageLoop;
 using std::deque;
 using std::string;
 
-const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
-const char kLibCrosProxyResolveName[] = "ProxyResolved";
-const char kLibCrosProxyResolveSignalInterface[] =
-    "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
-
 namespace {
 
-const int kTimeout = 5;  // seconds
+// Timeout for D-Bus calls in milliseconds.
+constexpr int kTimeoutMs = 5000;
 
 }  // namespace
 
 ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
-    LibCrosProxy* libcros_proxy)
-    : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
+    org::chromium::NetworkProxyServiceInterfaceProxyInterface* dbus_proxy)
+    : dbus_proxy_(dbus_proxy),
+      next_request_id_(kProxyRequestIdNull + 1),
+      weak_ptr_factory_(this) {}
 
-bool ChromeBrowserProxyResolver::Init() {
-  libcros_proxy_->ue_proxy_resolved_interface()
-      ->RegisterProxyResolvedSignalHandler(
-          base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
-                     base::Unretained(this)),
-          base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
-                     base::Unretained(this)));
-  return true;
-}
+ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() = default;
 
-ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
-  // Kill outstanding timers.
-  for (const auto& it : callbacks_) {
-    MessageLoop::current()->CancelTask(it.second->timeout_id);
-  }
-}
-
-ProxyRequestId ChromeBrowserProxyResolver::GetProxiesForUrl(
-    const string& url, const ProxiesResolvedFn& callback) {
-  int timeout = timeout_;
-  brillo::ErrorPtr error;
-  if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
-          url.c_str(),
-          kLibCrosProxyResolveSignalInterface,
-          kLibCrosProxyResolveName,
-          &error)) {
-    LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
-    timeout = 0;
-  }
-
-  std::unique_ptr<ProxyRequestData> request(new ProxyRequestData());
-  request->callback = callback;
-  ProxyRequestId timeout_id = MessageLoop::current()->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&ChromeBrowserProxyResolver::HandleTimeout,
-                 base::Unretained(this),
-                 url,
-                 request.get()),
-      TimeDelta::FromSeconds(timeout));
-  request->timeout_id = timeout_id;
-  callbacks_.emplace(url, std::move(request));
-
-  // We re-use the timeout_id from the MessageLoop as the request id.
-  return timeout_id;
-}
-
-bool ChromeBrowserProxyResolver::CancelProxyRequest(ProxyRequestId request) {
-  // Finding the timeout_id in the callbacks_ structure requires a linear search
-  // but we expect this operation to not be so frequent and to have just a few
-  // proxy requests, so this should be fast enough.
-  for (auto it = callbacks_.begin(); it != callbacks_.end(); ++it) {
-    if (it->second->timeout_id == request) {
-      MessageLoop::current()->CancelTask(request);
-      callbacks_.erase(it);
-      return true;
-    }
-  }
-  return false;
-}
-
-void ChromeBrowserProxyResolver::ProcessUrlResponse(
-    const string& source_url, const deque<string>& proxies) {
-  // Call all the occurrences of the |source_url| and erase them.
-  auto lower_end = callbacks_.lower_bound(source_url);
-  auto upper_end = callbacks_.upper_bound(source_url);
-  for (auto it = lower_end; it != upper_end; ++it) {
-    ProxyRequestData* request = it->second.get();
-    MessageLoop::current()->CancelTask(request->timeout_id);
-    request->callback.Run(proxies);
-  }
-  callbacks_.erase(lower_end, upper_end);
-}
-
-void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
-                                                   const string& signal_name,
-                                                   bool successful) {
-  if (!successful) {
-    LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
-               << signal_name;
-  }
-}
-
-void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
-    const string& source_url,
-    const string& proxy_info,
-    const string& error_message) {
-  if (!error_message.empty()) {
-    LOG(WARNING) << "ProxyResolved error: " << error_message;
-  }
-  ProcessUrlResponse(source_url, ParseProxyString(proxy_info));
-}
-
-void ChromeBrowserProxyResolver::HandleTimeout(string source_url,
-                                               ProxyRequestData* request) {
-  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
-  // Mark the timer_id that produced this callback as invalid to prevent
-  // canceling the timeout callback that already fired.
-  request->timeout_id = MessageLoop::kTaskIdNull;
-
-  deque<string> proxies = {kNoProxy};
-  ProcessUrlResponse(source_url, proxies);
-}
-
+// static
 deque<string> ChromeBrowserProxyResolver::ParseProxyString(
     const string& input) {
   deque<string> ret;
@@ -193,4 +89,50 @@
   return ret;
 }
 
-}  // namespace chromeos_update_engine
+ProxyRequestId ChromeBrowserProxyResolver::GetProxiesForUrl(
+    const string& url, const ProxiesResolvedFn& callback) {
+  const ProxyRequestId id = next_request_id_++;
+  dbus_proxy_->ResolveProxyAsync(
+      url,
+      base::Bind(&ChromeBrowserProxyResolver::OnResolveProxyResponse,
+                 weak_ptr_factory_.GetWeakPtr(), id),
+      base::Bind(&ChromeBrowserProxyResolver::OnResolveProxyError,
+                 weak_ptr_factory_.GetWeakPtr(), id),
+      kTimeoutMs);
+  pending_callbacks_[id] = callback;
+  return id;
+}
+
+bool ChromeBrowserProxyResolver::CancelProxyRequest(ProxyRequestId request) {
+  return pending_callbacks_.erase(request) != 0;
+}
+
+void ChromeBrowserProxyResolver::OnResolveProxyResponse(
+    ProxyRequestId request_id,
+    const std::string& proxy_info,
+    const std::string& error_message) {
+  if (!error_message.empty())
+    LOG(WARNING) << "Got error resolving proxy: " << error_message;
+  RunCallback(request_id, ParseProxyString(proxy_info));
+}
+
+void ChromeBrowserProxyResolver::OnResolveProxyError(ProxyRequestId request_id,
+                                                     brillo::Error* error) {
+  LOG(WARNING) << "Failed to resolve proxy: "
+               << (error ? error->GetMessage() : "[null]");
+  RunCallback(request_id, deque<string>{kNoProxy});
+}
+
+void ChromeBrowserProxyResolver::RunCallback(
+    ProxyRequestId request_id,
+    const std::deque<std::string>& proxies) {
+  auto it = pending_callbacks_.find(request_id);
+  if (it == pending_callbacks_.end())
+    return;
+
+  ProxiesResolvedFn callback = it->second;
+  pending_callbacks_.erase(it);
+  callback.Run(proxies);
+}
+
+} // namespace chromeos_update_engine