blob: 130c047fdcb664fa00918ad98750a332b8445fec [file] [log] [blame]
// Copyright 2015 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/ash/arc/intent_helper/arc_settings_service.h"
#include <string>
#include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "ash/components/arc/arc_features.h"
#include "ash/components/arc/arc_prefs.h"
#include "ash/components/arc/arc_util.h"
#include "ash/components/arc/mojom/backup_settings.mojom.h"
#include "ash/components/arc/mojom/pip.mojom.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/settings/timezone_settings.h"
#include "ash/constants/ash_pref_names.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/gtest_prod_util.h"
#include "base/json/json_writer.h"
#include "base/memory/singleton.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/arc/policy/arc_policy_util.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/settings/stats_reporting_controller.h"
#include "chrome/browser/ash/system/timezone_resolver_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
#include "chrome/common/pref_names.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_handler_observer.h"
#include "chromeos/network/onc/network_onc_utils.h"
#include "chromeos/network/proxy/proxy_config_service_impl.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/language/core/browser/pref_names.h"
#include "components/onc/onc_pref_names.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "components/proxy_config/proxy_prefs.h"
#include "net/base/url_util.h"
#include "net/proxy_resolution/proxy_bypass_rules.h"
#include "net/proxy_resolution/proxy_config.h"
#include "third_party/blink/public/common/page/page_zoom.h"
namespace {
using ::ash::system::TimezoneSettings;
constexpr char kSetFontScaleAction[] =
"org.chromium.arc.intent_helper.SET_FONT_SCALE";
constexpr char kSetPageZoomAction[] =
"org.chromium.arc.intent_helper.SET_PAGE_ZOOM";
constexpr char kSetProxyAction[] = "org.chromium.arc.intent_helper.SET_PROXY";
constexpr char kArcProxyBypassListDelimiter[] = ",";
constexpr float kAndroidFontScaleNormal = 1;
bool GetHttpProxyServer(const ProxyConfigDictionary* proxy_config_dict,
std::string* host,
int* port) {
std::string proxy_rules_string;
if (!proxy_config_dict->GetProxyServer(&proxy_rules_string))
return false;
net::ProxyConfig::ProxyRules proxy_rules;
proxy_rules.ParseFromString(proxy_rules_string);
const net::ProxyList* proxy_list = nullptr;
if (proxy_rules.type == net::ProxyConfig::ProxyRules::Type::PROXY_LIST) {
proxy_list = &proxy_rules.single_proxies;
} else if (proxy_rules.type ==
net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME) {
proxy_list = proxy_rules.MapUrlSchemeToProxyList(url::kHttpScheme);
}
if (!proxy_list || proxy_list->IsEmpty())
return false;
const net::ProxyServer& server = proxy_list->Get();
*host = server.host_port_pair().host();
*port = server.host_port_pair().port();
return !host->empty() && *port;
}
bool IsProxyAutoDetectionConfigured(const base::Value* proxy_config_dict) {
ProxyConfigDictionary dict(proxy_config_dict->Clone());
ProxyPrefs::ProxyMode mode;
dict.GetMode(&mode);
return mode == ProxyPrefs::MODE_AUTO_DETECT;
}
} // namespace
namespace arc {
namespace {
// Singleton factory for ArcSettingsService.
class ArcSettingsServiceFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcSettingsService,
ArcSettingsServiceFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcSettingsServiceFactory";
static ArcSettingsServiceFactory* GetInstance() {
return base::Singleton<ArcSettingsServiceFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcSettingsServiceFactory>;
ArcSettingsServiceFactory() = default;
~ArcSettingsServiceFactory() override = default;
};
} // namespace
// Listens to changes for select Chrome settings (prefs) that Android cares
// about and sends the new values to Android to keep the state in sync.
class ArcSettingsServiceImpl : public TimezoneSettings::Observer,
public ConnectionObserver<mojom::AppInstance>,
public chromeos::NetworkStateHandlerObserver {
public:
ArcSettingsServiceImpl(Profile* profile,
ArcBridgeService* arc_bridge_service);
ArcSettingsServiceImpl(const ArcSettingsServiceImpl&) = delete;
ArcSettingsServiceImpl& operator=(const ArcSettingsServiceImpl&) = delete;
~ArcSettingsServiceImpl() override;
// Called when a Chrome pref we have registered an observer for has changed.
// Obtains the new pref value and sends it to Android.
void OnPrefChanged(const std::string& pref_name) const;
// TimezoneSettings::Observer:
void TimezoneChanged(const icu::TimeZone& timezone) override;
// NetworkStateHandlerObserver:
void DefaultNetworkChanged(const chromeos::NetworkState* network) override;
// Retrieves Chrome's state for the settings that need to be synced on the
// initial Android boot and send it to Android. Called by ArcSettingsService.
void SyncInitialSettings() const;
private:
PrefService* GetPrefs() const { return profile_->GetPrefs(); }
// Returns whether kProxy pref proxy config is applied.
bool IsPrefProxyConfigApplied() const;
// Registers to observe changes for Chrome settings we care about.
void StartObservingSettingsChanges();
// Stops listening for Chrome settings changes.
void StopObservingSettingsChanges();
// Retrieves Chrome's state for the settings that need to be synced on each
// Android boot and send it to Android.
void SyncBootTimeSettings() const;
// Retrieves Chrome's state for the settings that need to be synced on each
// Android boot after AppInstance is ready and send it to Android.
void SyncAppTimeSettings();
// Send particular settings to Android.
// Keep these lines ordered lexicographically.
void SyncAccessibilityLargeMouseCursorEnabled() const;
void SyncAccessibilityVirtualKeyboardEnabled() const;
void SyncBackupEnabled() const;
void SyncDockedMagnifierEnabled() const;
void SyncFocusHighlightEnabled() const;
void SyncLocale() const;
void SyncLocationServiceEnabled() const;
void SyncProxySettings() const;
bool IsSystemProxyActive() const;
void SyncProxySettingsForSystemProxy() const;
void SyncReportingConsent(bool initial_sync) const;
void SyncPictureInPictureEnabled() const;
void SyncScreenMagnifierEnabled() const;
void SyncSelectToSpeakEnabled() const;
void SyncSpokenFeedbackEnabled() const;
void SyncSwitchAccessEnabled() const;
void SyncTimeZone() const;
void SyncTimeZoneByGeolocation() const;
void SyncUse24HourClock() const;
// Resets Android's font scale to the default value.
void ResetFontScaleToDefault() const;
// Resets Android's display density to the default value.
void ResetPageZoomToDefault() const;
// Registers to listen to a particular perf.
void AddPrefToObserve(const std::string& pref_name);
// Returns the integer value of the pref. pref_name must exist.
int GetIntegerPref(const std::string& pref_name) const;
// Gets whether this is a managed pref.
bool IsBooleanPrefManaged(const std::string& pref_name) const;
// Sends boolean pref broadcast to the delegate.
void SendBoolPrefSettingsBroadcast(const std::string& pref_name,
const std::string& action) const;
// Same as above, except sends a specific boolean value.
void SendBoolValueSettingsBroadcast(bool value,
bool managed,
const std::string& action) const;
// Sends a broadcast to the delegate.
void SendSettingsBroadcast(const std::string& action,
const base::DictionaryValue& extras) const;
// ConnectionObserver<mojom::AppInstance>:
void OnConnectionReady() override;
Profile* const profile_;
ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager.
// Manages pref observation registration.
PrefChangeRegistrar registrar_;
base::CallbackListSubscription reporting_consent_subscription_;
// Subscription for preference change of default zoom level. Subscription
// automatically unregisters a callback when it's destructed.
base::CallbackListSubscription default_zoom_level_subscription_;
// Name of the default network. Used to keep track of whether the default
// network has changed.
std::string default_network_name_;
// Proxy configuration of the default network.
base::Value default_proxy_config_;
// The PAC URL associated with `default_network_name_`, received via the DHCP
// discovery method.
GURL dhcp_wpad_url_;
};
ArcSettingsServiceImpl::ArcSettingsServiceImpl(
Profile* profile,
ArcBridgeService* arc_bridge_service)
: profile_(profile), arc_bridge_service_(arc_bridge_service) {
StartObservingSettingsChanges();
SyncBootTimeSettings();
// Note: if App connection is already established, OnConnectionReady()
// is synchronously called, so that initial sync is done in the method.
arc_bridge_service_->app()->AddObserver(this);
}
ArcSettingsServiceImpl::~ArcSettingsServiceImpl() {
StopObservingSettingsChanges();
arc_bridge_service_->app()->RemoveObserver(this);
}
void ArcSettingsServiceImpl::OnPrefChanged(const std::string& pref_name) const {
VLOG(1) << "OnPrefChanged: " << pref_name;
// Keep these lines ordered lexicographically by pref_name.
if (pref_name == onc::prefs::kDeviceOpenNetworkConfiguration ||
pref_name == onc::prefs::kOpenNetworkConfiguration) {
// Only update proxy settings if kProxy pref is not applied.
if (IsPrefProxyConfigApplied()) {
LOG(ERROR) << "Open Network Configuration proxy settings are not applied,"
<< " because kProxy preference is configured.";
return;
}
SyncProxySettings();
} else if (pref_name == ash::prefs::kAccessibilityFocusHighlightEnabled) {
SyncFocusHighlightEnabled();
} else if (pref_name == ash::prefs::kAccessibilityLargeCursorEnabled) {
SyncAccessibilityLargeMouseCursorEnabled();
} else if (pref_name == ash::prefs::kAccessibilityScreenMagnifierEnabled) {
SyncScreenMagnifierEnabled();
} else if (pref_name == ash::prefs::kAccessibilitySelectToSpeakEnabled) {
SyncSelectToSpeakEnabled();
} else if (pref_name == ash::prefs::kAccessibilitySpokenFeedbackEnabled) {
SyncSpokenFeedbackEnabled();
} else if (pref_name == ash::prefs::kAccessibilitySwitchAccessEnabled) {
SyncSwitchAccessEnabled();
} else if (pref_name == ash::prefs::kAccessibilityVirtualKeyboardEnabled) {
SyncAccessibilityVirtualKeyboardEnabled();
} else if (pref_name == ash::prefs::kDockedMagnifierEnabled) {
SyncDockedMagnifierEnabled();
} else if (pref_name == ::language::prefs::kApplicationLocale ||
pref_name == ::language::prefs::kPreferredLanguages) {
SyncLocale();
} else if (pref_name == ::prefs::kUse24HourClock) {
SyncUse24HourClock();
} else if (pref_name == ::prefs::kResolveTimezoneByGeolocationMethod) {
SyncTimeZoneByGeolocation();
} else if (pref_name == proxy_config::prefs::kProxy ||
pref_name == ::prefs::kSystemProxyUserTrafficHostAndPort) {
SyncProxySettings();
} else {
LOG(ERROR) << "Unknown pref changed.";
}
}
void ArcSettingsServiceImpl::TimezoneChanged(const icu::TimeZone& timezone) {
SyncTimeZone();
}
// This function is called when the default network changes or when any of its
// properties change. If the proxy configuration of the default network has
// changed, this method will call `SyncProxySettings` which syncs the proxy
// settings with ARC. Proxy changes on the default network are triggered by:
// - a user changing the proxy in the Network Settings UI;
// - ONC policy changes;
// - DHCP settings the WPAD URL via option 252.
void ArcSettingsServiceImpl::DefaultNetworkChanged(
const chromeos::NetworkState* network) {
if (!network)
return;
bool dhcp_wpad_url_changed =
dhcp_wpad_url_ != network->GetWebProxyAutoDiscoveryUrl();
dhcp_wpad_url_ = network->GetWebProxyAutoDiscoveryUrl();
if (IsPrefProxyConfigApplied()) {
// Normally, we would ignore proxy changes coming from the default
// network because the kProxy pref has priority. If the proxy is
// configured to use the Web Proxy Auto-Discovery (WPAD) Protocol via the
// DHCP discovery method, the PAC URL will be propagated to Chrome via the
// default network properties.
if (dhcp_wpad_url_changed && IsProxyAutoDetectionConfigured(GetPrefs()->Get(
proxy_config::prefs::kProxy))) {
SyncProxySettings();
}
return;
}
bool sync_proxy = false;
// Trigger a proxy settings sync to ARC if the default network changes.
if (default_network_name_ != network->name()) {
default_network_name_ = network->name();
default_proxy_config_ = base::Value();
sync_proxy = true;
}
// Trigger a proxy settings sync to ARC if the proxy configuration of the
// default network changes. Note: this code is only called if kProxy pref is
// not set.
if (default_proxy_config_ != network->proxy_config()) {
default_proxy_config_ = network->proxy_config().Clone();
sync_proxy = true;
}
// Check if proxy auto detection is enabled. If yes, and the PAC URL set via
// DHCP has changed, propagate the change to ARC.
if (!default_proxy_config_.is_none() && dhcp_wpad_url_changed &&
IsProxyAutoDetectionConfigured(&default_proxy_config_)) {
sync_proxy = true;
}
if (!sync_proxy)
return;
SyncProxySettings();
}
bool ArcSettingsServiceImpl::IsPrefProxyConfigApplied() const {
net::ProxyConfigWithAnnotation config;
return PrefProxyConfigTrackerImpl::PrefPrecedes(
PrefProxyConfigTrackerImpl::ReadPrefConfig(GetPrefs(), &config));
}
void ArcSettingsServiceImpl::StartObservingSettingsChanges() {
registrar_.Init(GetPrefs());
// Keep these lines ordered lexicographically.
AddPrefToObserve(ash::prefs::kAccessibilityFocusHighlightEnabled);
AddPrefToObserve(ash::prefs::kAccessibilityLargeCursorEnabled);
AddPrefToObserve(ash::prefs::kAccessibilityScreenMagnifierEnabled);
AddPrefToObserve(ash::prefs::kAccessibilitySelectToSpeakEnabled);
AddPrefToObserve(ash::prefs::kAccessibilitySpokenFeedbackEnabled);
AddPrefToObserve(ash::prefs::kAccessibilitySwitchAccessEnabled);
AddPrefToObserve(ash::prefs::kAccessibilityVirtualKeyboardEnabled);
AddPrefToObserve(ash::prefs::kDockedMagnifierEnabled);
AddPrefToObserve(::prefs::kResolveTimezoneByGeolocationMethod);
AddPrefToObserve(::prefs::kSystemProxyUserTrafficHostAndPort);
AddPrefToObserve(::prefs::kUse24HourClock);
AddPrefToObserve(proxy_config::prefs::kProxy);
AddPrefToObserve(onc::prefs::kDeviceOpenNetworkConfiguration);
AddPrefToObserve(onc::prefs::kOpenNetworkConfiguration);
// Note that some preferences, such as kArcBackupRestoreEnabled and
// kArcLocationServiceEnabled, are not dynamically updated after initial
// ARC setup and therefore are not observed here.
reporting_consent_subscription_ =
ash::StatsReportingController::Get()->AddObserver(
base::BindRepeating(&ArcSettingsServiceImpl::SyncReportingConsent,
base::Unretained(this), /*initial_sync=*/false));
TimezoneSettings::GetInstance()->AddObserver(this);
chromeos::NetworkHandler::Get()->network_state_handler()->AddObserver(
this, FROM_HERE);
}
void ArcSettingsServiceImpl::StopObservingSettingsChanges() {
registrar_.RemoveAll();
reporting_consent_subscription_ = {};
TimezoneSettings::GetInstance()->RemoveObserver(this);
chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
this, FROM_HERE);
}
void ArcSettingsServiceImpl::SyncInitialSettings() const {
// Keep these lines ordered lexicographically.
SyncBackupEnabled();
SyncLocationServiceEnabled();
SyncReportingConsent(/*initial_sync=*/true);
}
void ArcSettingsServiceImpl::SyncBootTimeSettings() const {
// Keep these lines ordered lexicographically.
SyncAccessibilityLargeMouseCursorEnabled();
SyncAccessibilityVirtualKeyboardEnabled();
SyncDockedMagnifierEnabled();
SyncFocusHighlightEnabled();
SyncProxySettings();
SyncReportingConsent(/*initial_sync=*/false);
SyncPictureInPictureEnabled();
SyncScreenMagnifierEnabled();
SyncSelectToSpeakEnabled();
SyncSpokenFeedbackEnabled();
SyncSwitchAccessEnabled();
SyncTimeZone();
SyncTimeZoneByGeolocation();
SyncUse24HourClock();
// Reset the values to default in case the user had a custom value.
// https://crbug.com/955071
ResetFontScaleToDefault();
ResetPageZoomToDefault();
}
void ArcSettingsServiceImpl::SyncAppTimeSettings() {
// Applying system locales change on ARC will cause restarting other services
// and applications on ARC and doing such change in early phase may lead to
// ARC OptIn failure b/65385376. So that observing preferred languages change
// should be deferred at least until |mojom::AppInstance| is ready.
// Note that locale and preferred languages are passed to Android container on
// boot time and current sync is redundant in most cases. However there is
// race when preferred languages may be changed during the ARC booting.
// Tracking and applying this information is complex task thar requires
// syncronization ARC session start, ArcSettingsService and
// ArcSettingsServiceImpl creation/destroying. Android framework has ability
// to supress reduntant calls so having this little overhead simplifies common
// implementation.
SyncLocale();
AddPrefToObserve(::language::prefs::kApplicationLocale);
AddPrefToObserve(::language::prefs::kPreferredLanguages);
}
void ArcSettingsServiceImpl::SyncAccessibilityLargeMouseCursorEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilityLargeCursorEnabled,
"org.chromium.arc.intent_helper.ACCESSIBILITY_LARGE_POINTER_ICON");
}
void ArcSettingsServiceImpl::SyncAccessibilityVirtualKeyboardEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilityVirtualKeyboardEnabled,
"org.chromium.arc.intent_helper.SET_SHOW_IME_WITH_HARD_KEYBOARD");
}
void ArcSettingsServiceImpl::SyncBackupEnabled() const {
auto* backup_settings = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->backup_settings(), SetBackupEnabled);
if (backup_settings) {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(prefs::kArcBackupRestoreEnabled);
DCHECK(pref);
const base::Value* value = pref->GetValue();
DCHECK(value->is_bool());
backup_settings->SetBackupEnabled(value->GetBool(),
!pref->IsUserModifiable());
}
}
void ArcSettingsServiceImpl::SyncFocusHighlightEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilityFocusHighlightEnabled,
"org.chromium.arc.intent_helper.SET_FOCUS_HIGHLIGHT_ENABLED");
}
void ArcSettingsServiceImpl::SyncScreenMagnifierEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilityScreenMagnifierEnabled,
"org.chromium.arc.intent_helper.SET_SCREEN_MAGNIFIER_ENABLED");
}
void ArcSettingsServiceImpl::SyncDockedMagnifierEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kDockedMagnifierEnabled,
"org.chromium.arc.intent_helper.SET_DOCKED_MAGNIFIER_ENABLED");
}
void ArcSettingsServiceImpl::SyncLocale() const {
if (IsArcLocaleSyncDisabled()) {
VLOG(1) << "Locale sync is disabled.";
return;
}
std::string locale;
std::string preferred_languages;
base::DictionaryValue extras;
// Chrome OS locale may contain only the language part (e.g. fr) but country
// code (e.g. fr_FR). Since Android expects locale to contain country code,
// ARC will derive a likely locale with country code from such
GetLocaleAndPreferredLanguages(profile_, &locale, &preferred_languages);
extras.SetStringKey("locale", locale);
extras.SetStringKey("preferredLanguages", preferred_languages);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_LOCALE", extras);
}
void ArcSettingsServiceImpl::SyncLocationServiceEnabled() const {
SendBoolPrefSettingsBroadcast(
prefs::kArcLocationServiceEnabled,
"org.chromium.arc.intent_helper.SET_LOCATION_SERVICE_ENABLED");
}
// TODO(b/159871128, hugobenichi, acostinas) The current implementation only
// syncs the global proxy from Chrome's default network settings. ARC has
// multi-network support so we should sync per-network proxy configuration.
void ArcSettingsServiceImpl::SyncProxySettings() const {
std::unique_ptr<ProxyConfigDictionary> proxy_config_dict =
chromeos::ProxyConfigServiceImpl::GetActiveProxyConfigDictionary(
GetPrefs(), g_browser_process->local_state());
ProxyPrefs::ProxyMode mode;
if (!proxy_config_dict || !proxy_config_dict->GetMode(&mode))
mode = ProxyPrefs::MODE_DIRECT;
if (mode != ProxyPrefs::MODE_DIRECT && IsSystemProxyActive()) {
SyncProxySettingsForSystemProxy();
return;
}
base::DictionaryValue extras;
extras.SetStringKey("mode", ProxyPrefs::ProxyModeToString(mode));
switch (mode) {
case ProxyPrefs::MODE_DIRECT:
break;
case ProxyPrefs::MODE_SYSTEM:
VLOG(1) << "The system mode is not translated.";
return;
case ProxyPrefs::MODE_AUTO_DETECT: {
// WPAD with DHCP has a higher priority than DNS.
if (dhcp_wpad_url_.is_valid()) {
extras.SetStringKey("pacUrl", dhcp_wpad_url_.spec());
} else {
// Fallback to WPAD via DNS.
extras.SetStringKey("pacUrl", "http://wpad/wpad.dat");
}
break;
}
case ProxyPrefs::MODE_PAC_SCRIPT: {
std::string pac_url;
if (!proxy_config_dict->GetPacUrl(&pac_url)) {
LOG(ERROR) << "No pac URL for pac_script proxy mode.";
return;
}
extras.SetStringKey("pacUrl", pac_url);
break;
}
case ProxyPrefs::MODE_FIXED_SERVERS: {
std::string host;
int port = 0;
if (!GetHttpProxyServer(proxy_config_dict.get(), &host, &port)) {
LOG(ERROR) << "No Http proxy server is sent.";
return;
}
extras.SetStringKey("host", host);
extras.SetInteger("port", port);
std::string bypass_list;
if (proxy_config_dict->GetBypassList(&bypass_list) &&
!bypass_list.empty()) {
// Chrome uses semicolon [;] as delimiter for the proxy bypass list
// while ARC expects comma [,] delimiter. Using the wrong delimiter
// causes loss of network connectivity for many apps in ARC.
auto bypassed_hosts = base::SplitStringPiece(
bypass_list, net::ProxyBypassRules::kBypassListDelimeter,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
bypass_list =
base::JoinString(bypassed_hosts, kArcProxyBypassListDelimiter);
extras.SetStringKey("bypassList", bypass_list);
}
break;
}
default:
LOG(ERROR) << "Incorrect proxy mode.";
return;
}
SendSettingsBroadcast(kSetProxyAction, extras);
}
bool ArcSettingsServiceImpl::IsSystemProxyActive() const {
if (!profile_->GetPrefs()->HasPrefPath(
::prefs::kSystemProxyUserTrafficHostAndPort)) {
return false;
}
const std::string proxy_host_and_port = profile_->GetPrefs()->GetString(
::prefs::kSystemProxyUserTrafficHostAndPort);
// System-proxy can be active, but the network namespace for the worker
// process is not yet configured.
return !proxy_host_and_port.empty();
}
void ArcSettingsServiceImpl::SyncProxySettingsForSystemProxy() const {
const std::string proxy_host_and_port = profile_->GetPrefs()->GetString(
::prefs::kSystemProxyUserTrafficHostAndPort);
std::string host;
int port;
if (!net::ParseHostAndPort(proxy_host_and_port, &host, &port))
return;
base::DictionaryValue extras;
extras.SetStringKey(
"mode", ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS));
extras.SetStringKey("host", host);
extras.SetInteger("port", port);
SendSettingsBroadcast(kSetProxyAction, extras);
}
void ArcSettingsServiceImpl::SyncReportingConsent(bool initial_sync) const {
bool consent = IsArcStatsReportingEnabled();
if (consent && !initial_sync && policy_util::IsAccountManaged(profile_)) {
// Don't enable reporting for managed users who might not have seen the
// reporting notice during ARC setup.
return;
}
if (consent && initial_sync &&
profile_->GetPrefs()->GetBoolean(prefs::kArcSkippedReportingNotice)) {
// Explicitly leave reporting off for users who did not get a reporting
// notice during setup, but otherwise would have reporting on due to the
// result of |IsArcStatsReportingEnabled()| during setup. Typically this is
// due to the fact that ArcSessionManager was able to skip the setup UI for
// managed users.
consent = false;
}
base::DictionaryValue extras;
extras.SetBoolean("reportingConsent", consent);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_REPORTING_CONSENT",
extras);
}
void ArcSettingsServiceImpl::SyncPictureInPictureEnabled() const {
bool isPipEnabled =
base::FeatureList::IsEnabled(arc::kPictureInPictureFeature);
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->pip(),
SetPipSuppressionStatus);
if (!instance)
return;
instance->SetPipSuppressionStatus(!isPipEnabled);
}
void ArcSettingsServiceImpl::SyncSelectToSpeakEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilitySelectToSpeakEnabled,
"org.chromium.arc.intent_helper.SET_SELECT_TO_SPEAK_ENABLED");
}
void ArcSettingsServiceImpl::SyncSpokenFeedbackEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilitySpokenFeedbackEnabled,
"org.chromium.arc.intent_helper.SET_SPOKEN_FEEDBACK_ENABLED");
}
void ArcSettingsServiceImpl::SyncSwitchAccessEnabled() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kAccessibilitySwitchAccessEnabled,
"org.chromium.arc.intent_helper.SET_SWITCH_ACCESS_ENABLED");
}
void ArcSettingsServiceImpl::SyncTimeZone() const {
TimezoneSettings* timezone_settings = TimezoneSettings::GetInstance();
std::u16string timezoneID = timezone_settings->GetCurrentTimezoneID();
base::DictionaryValue extras;
extras.SetStringKey("olsonTimeZone", timezoneID);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_TIME_ZONE", extras);
}
void ArcSettingsServiceImpl::SyncTimeZoneByGeolocation() const {
base::DictionaryValue extras;
extras.SetBoolean("autoTimeZone",
chromeos::system::TimeZoneResolverManager::
GetEffectiveUserTimeZoneResolveMethod(
registrar_.prefs(), false) !=
chromeos::system::TimeZoneResolverManager::
TimeZoneResolveMethod::DISABLED);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_AUTO_TIME_ZONE",
extras);
}
void ArcSettingsServiceImpl::SyncUse24HourClock() const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(::prefs::kUse24HourClock);
DCHECK(pref);
DCHECK(pref->GetValue()->is_bool());
bool use24HourClock = pref->GetValue()->GetBool();
base::DictionaryValue extras;
extras.SetBoolean("use24HourClock", use24HourClock);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_USE_24_HOUR_CLOCK",
extras);
}
void ArcSettingsServiceImpl::ResetFontScaleToDefault() const {
base::DictionaryValue extras;
extras.SetDoubleKey("scale", kAndroidFontScaleNormal);
SendSettingsBroadcast(kSetFontScaleAction, extras);
}
void ArcSettingsServiceImpl::ResetPageZoomToDefault() const {
base::DictionaryValue extras;
extras.SetDoubleKey("zoomFactor", 1.0);
SendSettingsBroadcast(kSetPageZoomAction, extras);
}
void ArcSettingsServiceImpl::AddPrefToObserve(const std::string& pref_name) {
registrar_.Add(pref_name,
base::BindRepeating(&ArcSettingsServiceImpl::OnPrefChanged,
base::Unretained(this)));
}
int ArcSettingsServiceImpl::GetIntegerPref(const std::string& pref_name) const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(pref_name);
DCHECK(pref);
DCHECK(pref->GetValue()->is_int());
return pref->GetValue()->GetIfInt().value_or(-1);
}
bool ArcSettingsServiceImpl::IsBooleanPrefManaged(
const std::string& pref_name) const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(pref_name);
DCHECK(pref);
bool value_exists = pref->GetValue()->is_bool();
DCHECK(value_exists);
return !pref->IsUserModifiable();
}
void ArcSettingsServiceImpl::SendBoolPrefSettingsBroadcast(
const std::string& pref_name,
const std::string& action) const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(pref_name);
DCHECK(pref);
DCHECK(pref->GetValue()->is_bool());
bool enabled = pref->GetValue()->GetBool();
SendBoolValueSettingsBroadcast(enabled, !pref->IsUserModifiable(), action);
}
void ArcSettingsServiceImpl::SendBoolValueSettingsBroadcast(
bool enabled,
bool managed,
const std::string& action) const {
base::DictionaryValue extras;
extras.SetBoolean("enabled", enabled);
extras.SetBoolean("managed", managed);
SendSettingsBroadcast(action, extras);
}
void ArcSettingsServiceImpl::SendSettingsBroadcast(
const std::string& action,
const base::DictionaryValue& extras) const {
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->intent_helper(), SendBroadcast);
if (!instance)
return;
std::string extras_json;
bool write_success = base::JSONWriter::Write(extras, &extras_json);
DCHECK(write_success);
instance->SendBroadcast(
action, ArcIntentHelperBridge::kArcIntentHelperPackageName,
ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
"SettingsReceiver"),
extras_json);
}
// ConnectionObserver<mojom::AppInstance>:
void ArcSettingsServiceImpl::OnConnectionReady() {
arc_bridge_service_->app()->RemoveObserver(this);
SyncAppTimeSettings();
}
// static
ArcSettingsService* ArcSettingsService::GetForBrowserContext(
content::BrowserContext* context) {
return ArcSettingsServiceFactory::GetForBrowserContext(context);
}
ArcSettingsService::ArcSettingsService(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: profile_(Profile::FromBrowserContext(context)),
arc_bridge_service_(bridge_service) {
arc_bridge_service_->intent_helper()->AddObserver(this);
ArcSessionManager::Get()->AddObserver(this);
if (!IsArcPlayStoreEnabledForProfile(profile_))
SetInitialSettingsPending(false);
}
ArcSettingsService::~ArcSettingsService() {
ArcSessionManager::Get()->RemoveObserver(this);
arc_bridge_service_->intent_helper()->RemoveObserver(this);
}
void ArcSettingsService::OnConnectionReady() {
impl_ =
std::make_unique<ArcSettingsServiceImpl>(profile_, arc_bridge_service_);
if (!IsInitialSettingsPending())
return;
impl_->SyncInitialSettings();
SetInitialSettingsPending(false);
}
void ArcSettingsService::OnConnectionClosed() {
impl_.reset();
}
void ArcSettingsService::OnArcPlayStoreEnabledChanged(bool enabled) {
if (!enabled)
SetInitialSettingsPending(false);
}
void ArcSettingsService::OnArcInitialStart() {
DCHECK(!IsInitialSettingsPending());
if (!impl_) {
SetInitialSettingsPending(true);
return;
}
impl_->SyncInitialSettings();
}
void ArcSettingsService::SetInitialSettingsPending(bool pending) {
profile_->GetPrefs()->SetBoolean(prefs::kArcInitialSettingsPending, pending);
}
bool ArcSettingsService::IsInitialSettingsPending() const {
return profile_->GetPrefs()->GetBoolean(prefs::kArcInitialSettingsPending);
}
} // namespace arc