[Lacros] Propagate proxy settings from Ash-Chrome

Proxy settings on different platforms are monitored using platform
specific implementations of ProxyConfigService
(i.e. ProxyConfigServiceMac, ProxyConfigServiceWin,
ProxyConfigServiceLinux etc.) which listens/polls proxy configs from
the OS.

This CL introduces the ProxyConfigServiceLacros which listens for
proxy settings coming from Ash-Chrome via the mojo crosapi. The proxy
configs are propagated to the browser's NetworkService only if the
pref kUseAshProxy is true. If kUseAshProxy is false, the proxy config
sent by Ash-Chrome is not used by the NetworkService.

To keep the existing order of precedence, if a proxy is configured via
an extension, it will have priority over the proxy config coming from
Ash-Chrome, regardless of the value of kUseAshProxy.

Not forcing secondary profiles to go through the system proxy is a
privacy improvement, especially in a commercial deployment. In a
follow-up CL we will add a UI checkbox to allow secondary profiles to
opt-in on using the system proxy. For now, kUseAshProxy is always true
for primary profiles and false for secondary profiles.

Bug: b/192914999
Test: unit tests, browser test
Change-Id: Icac55ce896caf0afe789b7a86c6b93093c1ed444
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3122991
Reviewed-by: Dominic Battré <[email protected]>
Reviewed-by: Roland Bock <[email protected]>
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Pavol Marko <[email protected]>
Commit-Queue: Andreea-Elena Costinas <[email protected]>
Cr-Commit-Position: refs/heads/main@{#923876}
diff --git a/chrome/browser/lacros/net/proxy_config_service_lacros.cc b/chrome/browser/lacros/net/proxy_config_service_lacros.cc
new file mode 100644
index 0000000..fc3b156
--- /dev/null
+++ b/chrome/browser/lacros/net/proxy_config_service_lacros.cc
@@ -0,0 +1,115 @@
+// Copyright 2021 The Chromium 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 "chrome/browser/lacros/net/proxy_config_service_lacros.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/lacros/net/network_settings_translation.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "net/proxy_resolution/proxy_config.h"
+#include "net/proxy_resolution/proxy_config_with_annotation.h"
+
+namespace {
+
+net::ProxyConfigWithAnnotation GetConfigOrDirect(
+    const absl::optional<net::ProxyConfigWithAnnotation>& optional_config,
+    PrefService* pref_service) {
+  if (!optional_config || !pref_service ||
+      !pref_service->GetBoolean(prefs::kUseAshProxy)) {
+    net::ProxyConfigWithAnnotation config =
+        net::ProxyConfigWithAnnotation::CreateDirect();
+    return config;
+  }
+  return optional_config.value();
+}
+}  // namespace
+
+namespace chromeos {
+
+ProxyConfigServiceLacros::ProxyConfigServiceLacros(Profile* profile) {
+  DCHECK(profile);
+  auto* lacros_service = chromeos::LacrosService::Get();
+  // crosapi is disabled in browser_tests.
+  if (!lacros_service->IsAvailable<crosapi::mojom::NetworkSettingsService>()) {
+    LOG(ERROR) << "The NetworkSettingsService service is not available";
+    return;
+  }
+  lacros_service->GetRemote<crosapi::mojom::NetworkSettingsService>()
+      ->AddNetworkSettingsObserver(receiver_.BindNewPipeAndPassRemote());
+
+  // `kUseAshProxy` is a user exposed setting whether to use the ash proxy (from
+  // the system) or whether to use profile specific proxy settings. This option
+  // is only given for secondary profiles. For the primary profile, the user has
+  // to use the system proxy settings, the value of kUseAshProxy is always true,
+  // and the setting is not exposed to the user.
+  // TODO(acostinas, b:192915915) Enable secondary profiles to configure
+  // `kUseAshProxy` from chrome://settings.
+  if (profile->IsMainProfile()) {
+    profile->GetPrefs()->SetBoolean(prefs::kUseAshProxy, true);
+  }
+
+  profile_prefs_ = profile->GetPrefs();
+  profile_pref_change_registrar_.Init(profile_prefs_);
+  profile_pref_change_registrar_.Add(
+      prefs::kUseAshProxy,
+      base::BindRepeating(&ProxyConfigServiceLacros::OnUseAshProxyPrefChanged,
+                          base::Unretained(this)));
+}
+
+ProxyConfigServiceLacros::~ProxyConfigServiceLacros() = default;
+
+void ProxyConfigServiceLacros::OnProxyChanged(
+    crosapi::mojom::ProxyConfigPtr proxy_config) {
+  cached_config_ = CrosapiProxyToNetProxy(std::move(proxy_config));
+  NotifyObservers();
+}
+
+void ProxyConfigServiceLacros::OnUseAshProxyPrefChanged() {
+  NotifyObservers();
+}
+
+void ProxyConfigServiceLacros::NotifyObservers() {
+  for (auto& observer : observers_) {
+    observer.OnProxyConfigChanged(
+        GetConfigOrDirect(cached_config_, profile_prefs_),
+        ConfigAvailability::CONFIG_VALID);
+  }
+}
+
+void ProxyConfigServiceLacros::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void ProxyConfigServiceLacros::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+// static
+void ProxyConfigServiceLacros::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kUseAshProxy, false);
+}
+
+net::ProxyConfigService::ConfigAvailability
+ProxyConfigServiceLacros::GetLatestProxyConfig(
+    net::ProxyConfigWithAnnotation* config) {
+  // Returns the last proxy configuration that the Ash
+  // NetworkSettingsService notified us, or the direct proxy if `cached_config_`
+  // is not set.
+  *config = GetConfigOrDirect(cached_config_, profile_prefs_);
+  return ConfigAvailability::CONFIG_VALID;
+}
+
+}  // namespace chromeos