#include "chrome/browser/ash/arc/intent_helper/arc_settings_service.h"
#include <string>
#include <string_view>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/system/privacy_hub/privacy_hub_controller.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/gtest_prod_util.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.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/global_features.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "chromeos/ash/components/network/onc/network_onc_utils.h"
#include "chromeos/ash/components/network/proxy/proxy_config_service_impl.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
#include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h"
#include "chromeos/ash/experiences/arc/arc_features.h"
#include "chromeos/ash/experiences/arc/arc_prefs.h"
#include "chromeos/ash/experiences/arc/arc_util.h"
#include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_bridge.h"
#include "chromeos/ash/experiences/arc/intent_helper/arc_intent_helper_package.h"
#include "chromeos/ash/experiences/arc/mojom/backup_settings.mojom.h"
#include "chromeos/ash/experiences/arc/mojom/intent_helper.mojom.h"
#include "chromeos/ash/experiences/arc/mojom/pip.mojom.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "components/language/core/browser/pref_names.h"
#include "components/live_caption/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_config.h"
#include "net/proxy_resolution/proxy_host_matching_rules.h"
#include "third_party/blink/public/common/page/page_zoom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/metadata/base_type_conversion.h"
#undef ENABLED_VLOG_LEVEL
#define ENABLED_VLOG_LEVEL 1
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;
constexpr char kTextShadowRaised[] = "-2px -2px 4px rgba(0, 0, 0, 0.5)";
constexpr char kTextShadowDepressed[] = "2px 2px 4px rgba(0, 0, 0, 0.5)";
constexpr char kTextShadowUniform[] =
"-1px 0px 0px black, 0px -1px 0px black, 1px 0px 0px black, 0px 1px 0px "
"black";
constexpr char kTextShadowDropShadow[] =
"0px 0px 2px rgba(0, 0, 0, 0.5), 2px 2px 2px black";
arc::mojom::CaptionColorPtr GetCaptionColorFromPrefs(
const PrefService* prefs,
const char* color_pref_name,
const char* opacity_pref_name) {
const std::string rgb = prefs->GetString(color_pref_name);
if (rgb.empty()) {
return nullptr;
}
const int opacity = prefs->GetInteger(opacity_pref_name);
std::string color_str =
base::StringPrintf("rgba(%s,%s)", rgb.c_str(),
base::NumberToString(opacity / 100.0).c_str());
std::optional<SkColor> sk_color =
ui::metadata::SkColorConverter::FromString(base::UTF8ToUTF16(color_str));
if (!sk_color) {
return nullptr;
}
SkColor color = sk_color.value();
return arc::mojom::CaptionColor::New(SkColorGetA(color), SkColorGetR(color),
SkColorGetG(color), SkColorGetB(color));
}
float GetFontScaleFromPref(const PrefService* prefs) {
std::string text_size =
prefs->GetString(::prefs::kAccessibilityCaptionsTextSize);
if (text_size.empty()) {
return 1.0f;
}
CHECK(text_size[text_size.size() - 1] == '%');
text_size = text_size.substr(0, text_size.size() - 1);
int font_scale;
CHECK(base::StringToInt(text_size, &font_scale));
return font_scale / 100.0f;
}
arc::mojom::CaptionStylePtr GetCaptionStyleFromPrefs(const PrefService* prefs) {
CHECK(prefs);
arc::mojom::CaptionStylePtr style = arc::mojom::CaptionStyle::New();
style->font_scale = GetFontScaleFromPref(prefs);
style->text_color =
GetCaptionColorFromPrefs(prefs, ::prefs::kAccessibilityCaptionsTextColor,
::prefs::kAccessibilityCaptionsTextOpacity);
style->background_color = GetCaptionColorFromPrefs(
prefs, ::prefs::kAccessibilityCaptionsBackgroundColor,
::prefs::kAccessibilityCaptionsBackgroundOpacity);
style->user_locale = prefs->GetString(::language::prefs::kApplicationLocale);
const std::string text_shadow =
prefs->GetString(::prefs::kAccessibilityCaptionsTextShadow);
if (text_shadow == kTextShadowRaised) {
style->text_shadow_type = arc::mojom::CaptionTextShadowType::kRaised;
} else if (text_shadow == kTextShadowDepressed) {
style->text_shadow_type = arc::mojom::CaptionTextShadowType::kDepressed;
} else if (text_shadow == kTextShadowUniform) {
style->text_shadow_type = arc::mojom::CaptionTextShadowType::kUniform;
} else if (text_shadow == kTextShadowDropShadow) {
style->text_shadow_type = arc::mojom::CaptionTextShadowType::kDropShadow;
} else {
style->text_shadow_type = arc::mojom::CaptionTextShadowType::kNone;
}
return style;
}
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::ProxyChain& chain = proxy_list->First();
CHECK(chain.is_single_proxy());
const net::ProxyServer& server = chain.First();
*host = server.host_port_pair().host();
*port = server.host_port_pair().port();
return !host->empty() && *port;
}
bool IsProxyAutoDetectionConfigured(
const base::Value::Dict& proxy_config_dict) {
ProxyConfigDictionary dict(proxy_config_dict.Clone());
ProxyPrefs::ProxyMode mode;
dict.GetMode(&mode);
return mode == ProxyPrefs::MODE_AUTO_DETECT;
}
}
namespace arc {
namespace {
class ArcSettingsServiceFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcSettingsService,
ArcSettingsServiceFactory> {
public:
static constexpr const char* kName = "ArcSettingsServiceFactory";
static ArcSettingsServiceFactory* GetInstance() {
return base::Singleton<ArcSettingsServiceFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcSettingsServiceFactory>;
ArcSettingsServiceFactory() = default;
~ArcSettingsServiceFactory() override = default;
};
}
class ArcSettingsServiceImpl : public TimezoneSettings::Observer,
public ConnectionObserver<mojom::AppInstance>,
public ash::NetworkStateHandlerObserver {
public:
ArcSettingsServiceImpl(Profile* profile,
ArcBridgeService* arc_bridge_service);
ArcSettingsServiceImpl(const ArcSettingsServiceImpl&) = delete;
ArcSettingsServiceImpl& operator=(const ArcSettingsServiceImpl&) = delete;
~ArcSettingsServiceImpl() override;
void OnPrefChanged(const std::string& pref_name) const;
void TimezoneChanged(const icu::TimeZone& timezone) override;
void DefaultNetworkChanged(const ash::NetworkState* network) override;
void SyncInitialSettings() const;
private:
PrefService* GetPrefs() const { return profile_->GetPrefs(); }
bool IsPrefProxyConfigApplied() const;
void StartObservingSettingsChanges();
void StopObservingSettingsChanges();
void SyncBootTimeSettings() const;
void SyncAppTimeSettings();
void SyncAccessibilityLargeMouseCursorEnabled() const;
void SyncAccessibilityFeatures() const;
void SyncAccessibilityVirtualKeyboardEnabled() const;
void SyncBackupEnabled() const;
void SyncCaptionStyle() const;
void SyncConsumerAutoUpdateToggle() 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 SyncTimeZone() const;
void SyncTimeZoneByGeolocation() const;
void SyncUse24HourClock() const;
void SyncUserGeolocation() const;
void SyncUserGeolocationAccuracy() const;
void ResetFontScaleToDefault() const;
void ResetPageZoomToDefault() const;
void AddPrefToObserve(const std::string& pref_name);
void AddLocalStatePrefToObserve(const std::string& pref_name);
int GetIntegerPref(const std::string& pref_name) const;
bool GetBooleanPref(const std::string& pref_name) const;
bool IsBooleanPrefManaged(const std::string& pref_name) const;
void SendBoolPrefSettingsBroadcast(const std::string& pref_name,
const std::string& action) const;
void SendBoolLocalStatePrefSettingsBroadcast(const std::string& pref_name,
const std::string& action) const;
void SendBoolValueSettingsBroadcast(bool value,
bool managed,
const std::string& action) const;
void SendSettingsBroadcast(const std::string& action,
const base::Value::Dict& extras) const;
void OnConnectionReady() override;
const raw_ptr<Profile> profile_;
const raw_ptr<ArcBridgeService>
arc_bridge_service_;
PrefChangeRegistrar registrar_;
PrefChangeRegistrar local_state_registrar_;
base::CallbackListSubscription reporting_consent_subscription_;
base::CallbackListSubscription default_zoom_level_subscription_;
base::ScopedObservation<ash::NetworkStateHandler,
ash::NetworkStateHandlerObserver>
network_state_handler_observer_{this};
std::string default_network_name_;
std::optional<base::Value::Dict> default_proxy_config_;
GURL dhcp_wpad_url_;
};
ArcSettingsServiceImpl::ArcSettingsServiceImpl(
Profile* profile,
ArcBridgeService* arc_bridge_service)
: profile_(profile), arc_bridge_service_(arc_bridge_service) {
StartObservingSettingsChanges();
SyncBootTimeSettings();
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;
if (pref_name == onc::prefs::kDeviceOpenNetworkConfiguration ||
pref_name == onc::prefs::kOpenNetworkConfiguration) {
if (IsPrefProxyConfigApplied()) {
LOG(ERROR) << "Open Network Configuration proxy settings are not applied,"
<< " because kProxy preference is configured.";
return;
}
SyncProxySettings();
} else if (pref_name == ::prefs::kAccessibilityCaptionsBackgroundColor ||
pref_name == ::prefs::kAccessibilityCaptionsBackgroundOpacity ||
pref_name == ::prefs::kAccessibilityCaptionsTextColor ||
pref_name == ::prefs::kAccessibilityCaptionsTextFont ||
pref_name == ::prefs::kAccessibilityCaptionsTextOpacity ||
pref_name == ::prefs::kAccessibilityCaptionsTextShadow ||
pref_name == ::prefs::kAccessibilityCaptionsTextSize) {
SyncCaptionStyle();
} else if (pref_name == ash::prefs::kAccessibilityFocusHighlightEnabled ||
pref_name == ash::prefs::kAccessibilityScreenMagnifierEnabled ||
pref_name == ash::prefs::kAccessibilitySelectToSpeakEnabled ||
pref_name == ash::prefs::kAccessibilitySpokenFeedbackEnabled ||
pref_name == ash::prefs::kAccessibilitySwitchAccessEnabled ||
pref_name == ash::prefs::kDockedMagnifierEnabled) {
SyncAccessibilityFeatures();
} else if (pref_name == ash::prefs::kAccessibilityLargeCursorEnabled) {
SyncAccessibilityLargeMouseCursorEnabled();
} else if (pref_name == ash::prefs::kAccessibilityVirtualKeyboardEnabled) {
SyncAccessibilityVirtualKeyboardEnabled();
} else if (pref_name == ash::prefs::kUserGeolocationAccessLevel) {
SyncUserGeolocation();
} else if (pref_name == ash::prefs::kUserGeolocationAccuracyEnabled) {
SyncUserGeolocationAccuracy();
} else if (pref_name == ::language::prefs::kApplicationLocale ||
pref_name == ::language::prefs::kPreferredLanguages) {
SyncLocale();
if (pref_name == ::language::prefs::kApplicationLocale) {
SyncCaptionStyle();
}
} else if (pref_name == ::prefs::kConsumerAutoUpdateToggle) {
SyncConsumerAutoUpdateToggle();
} 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();
}
void ArcSettingsServiceImpl::DefaultNetworkChanged(
const ash::NetworkState* network) {
if (!network)
return;
bool dhcp_wpad_url_changed =
dhcp_wpad_url_ != network->GetWebProxyAutoDiscoveryUrl();
dhcp_wpad_url_ = network->GetWebProxyAutoDiscoveryUrl();
if (IsPrefProxyConfigApplied()) {
if (dhcp_wpad_url_changed) {
const base::Value& proxy =
GetPrefs()->GetValue(proxy_config::prefs::kProxy);
if (proxy.is_dict() && IsProxyAutoDetectionConfigured(proxy.GetDict())) {
SyncProxySettings();
}
}
return;
}
bool sync_proxy = false;
if (default_network_name_ != network->name()) {
default_network_name_ = network->name();
default_proxy_config_.reset();
sync_proxy = true;
}
if (default_proxy_config_ != network->proxy_config()) {
if (network->proxy_config()) {
default_proxy_config_ = network->proxy_config()->Clone();
} else {
default_proxy_config_.reset();
}
sync_proxy = true;
}
if (default_proxy_config_.has_value() && dhcp_wpad_url_changed &&
IsProxyAutoDetectionConfigured(default_proxy_config_.value())) {
sync_proxy = true;
}
if (network->GetVpnProviderType() == shill::kProviderArcVpn) {
sync_proxy = false;
}
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());
local_state_registrar_.Init(g_browser_process->local_state());
AddPrefToObserve(::prefs::kAccessibilityCaptionsBackgroundColor);
AddPrefToObserve(::prefs::kAccessibilityCaptionsBackgroundOpacity);
AddPrefToObserve(::prefs::kAccessibilityCaptionsTextColor);
AddPrefToObserve(::prefs::kAccessibilityCaptionsTextFont);
AddPrefToObserve(::prefs::kAccessibilityCaptionsTextOpacity);
AddPrefToObserve(::prefs::kAccessibilityCaptionsTextShadow);
AddPrefToObserve(::prefs::kAccessibilityCaptionsTextSize);
AddPrefToObserve(::prefs::kResolveTimezoneByGeolocationMethod);
AddPrefToObserve(::prefs::kSystemProxyUserTrafficHostAndPort);
AddPrefToObserve(::prefs::kUse24HourClock);
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(ash::prefs::kUserGeolocationAccessLevel);
AddPrefToObserve(ash::prefs::kUserGeolocationAccuracyEnabled);
AddPrefToObserve(onc::prefs::kDeviceOpenNetworkConfiguration);
AddPrefToObserve(onc::prefs::kOpenNetworkConfiguration);
AddPrefToObserve(proxy_config::prefs::kProxy);
AddLocalStatePrefToObserve(::prefs::kConsumerAutoUpdateToggle);
reporting_consent_subscription_ =
ash::StatsReportingController::Get()->AddObserver(
base::BindRepeating(&ArcSettingsServiceImpl::SyncReportingConsent,
base::Unretained(this), false));
TimezoneSettings::GetInstance()->AddObserver(this);
network_state_handler_observer_.Observe(
ash::NetworkHandler::Get()->network_state_handler());
}
void ArcSettingsServiceImpl::StopObservingSettingsChanges() {
registrar_.RemoveAll();
local_state_registrar_.RemoveAll();
reporting_consent_subscription_ = {};
TimezoneSettings::GetInstance()->RemoveObserver(this);
network_state_handler_observer_.Reset();
}
void ArcSettingsServiceImpl::SyncInitialSettings() const {
SyncBackupEnabled();
SyncLocationServiceEnabled();
SyncReportingConsent(true);
}
void ArcSettingsServiceImpl::SyncBootTimeSettings() const {
SyncAccessibilityLargeMouseCursorEnabled();
SyncAccessibilityFeatures();
SyncAccessibilityVirtualKeyboardEnabled();
SyncCaptionStyle();
SyncConsumerAutoUpdateToggle();
SyncProxySettings();
SyncReportingConsent(false);
SyncPictureInPictureEnabled();
SyncTimeZone();
SyncTimeZoneByGeolocation();
SyncUse24HourClock();
ResetFontScaleToDefault();
ResetPageZoomToDefault();
}
void ArcSettingsServiceImpl::SyncAppTimeSettings() {
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::SyncCaptionStyle() const {
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->intent_helper(), SetCaptionStyle);
if (!instance) {
return;
}
const PrefService* pref_service = registrar_.prefs();
CHECK(pref_service);
arc::mojom::CaptionStylePtr caption_style =
GetCaptionStyleFromPrefs(pref_service);
CHECK(caption_style);
instance->SetCaptionStyle(std::move(caption_style));
}
void ArcSettingsServiceImpl::SyncAccessibilityFeatures() const {
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->intent_helper(), EnableAccessibilityFeatures);
if (!instance) {
return;
}
arc::mojom::AccessibilityFeaturesPtr features =
arc::mojom::AccessibilityFeatures::New();
features->docked_magnifier_enabled =
GetBooleanPref(ash::prefs::kDockedMagnifierEnabled);
features->focus_highlight_enabled =
GetBooleanPref(ash::prefs::kAccessibilityFocusHighlightEnabled);
features->screen_magnifier_enabled =
GetBooleanPref(ash::prefs::kAccessibilityScreenMagnifierEnabled);
features->select_to_speak_enabled =
GetBooleanPref(ash::prefs::kAccessibilitySelectToSpeakEnabled);
features->spoken_feedback_enabled =
GetBooleanPref(ash::prefs::kAccessibilitySpokenFeedbackEnabled);
features->switch_access_enabled =
GetBooleanPref(ash::prefs::kAccessibilitySwitchAccessEnabled);
instance->EnableAccessibilityFeatures(std::move(features));
}
void ArcSettingsServiceImpl::SyncLocale() const {
if (IsArcLocaleSyncDisabled()) {
VLOG(1) << "Locale sync is disabled.";
return;
}
const ApplicationLocaleStorage& application_locale_storage = CHECK_DEREF(
g_browser_process->GetFeatures()->application_locale_storage());
std::string locale;
std::string preferred_languages;
GetLocaleAndPreferredLanguages(application_locale_storage, profile_, &locale,
&preferred_languages);
base::Value::Dict extras;
extras.Set("locale", locale);
extras.Set("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");
}
void ArcSettingsServiceImpl::SyncProxySettings() const {
std::unique_ptr<ProxyConfigDictionary> proxy_config_dict =
ash::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::Value::Dict extras;
extras.Set("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: {
if (dhcp_wpad_url_.is_valid()) {
extras.Set("pacUrl", dhcp_wpad_url_.spec());
} else {
extras.Set("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.Set("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.Set("host", host);
extras.Set("port", port);
std::string bypass_list;
if (proxy_config_dict->GetBypassList(&bypass_list) &&
!bypass_list.empty()) {
auto bypassed_hosts = base::SplitStringPiece(
bypass_list, net::ProxyHostMatchingRules::kBypassListDelimeter,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
bypass_list =
base::JoinString(bypassed_hosts, kArcProxyBypassListDelimiter);
extras.Set("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);
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::Value::Dict extras;
extras.Set("mode",
ProxyPrefs::ProxyModeToString(ProxyPrefs::MODE_FIXED_SERVERS));
extras.Set("host", host);
extras.Set("port", port);
SendSettingsBroadcast(kSetProxyAction, extras);
}
void ArcSettingsServiceImpl::SyncReportingConsent(bool initial_sync) const {
bool consent = IsArcStatsReportingEnabled();
if (consent && !initial_sync && policy_util::IsAccountManaged(profile_)) {
return;
}
if (consent && initial_sync &&
profile_->GetPrefs()->GetBoolean(prefs::kArcSkippedReportingNotice)) {
consent = false;
}
base::Value::Dict extras;
extras.Set("reportingConsent", consent);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_REPORTING_CONSENT",
extras);
}
void ArcSettingsServiceImpl::SyncPictureInPictureEnabled() const {
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->pip(),
SetPipSuppressionStatus);
if (!instance)
return;
instance->SetPipSuppressionStatus(false);
}
void ArcSettingsServiceImpl::SyncTimeZone() const {
TimezoneSettings* timezone_settings = TimezoneSettings::GetInstance();
std::u16string timezoneID = timezone_settings->GetCurrentTimezoneID();
base::Value::Dict extras;
extras.Set("olsonTimeZone", timezoneID);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_TIME_ZONE", extras);
}
void ArcSettingsServiceImpl::SyncTimeZoneByGeolocation() const {
base::Value::Dict extras;
extras.Set("autoTimeZone", ash::system::TimeZoneResolverManager::
GetEffectiveUserTimeZoneResolveMethod(
registrar_.prefs(), false) !=
ash::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::Value::Dict extras;
extras.Set("use24HourClock", use24HourClock);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_USE_24_HOUR_CLOCK",
extras);
}
void ArcSettingsServiceImpl::SyncUserGeolocation() const {
const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
ash::prefs::kUserGeolocationAccessLevel);
DCHECK(pref);
DCHECK(pref->GetValue()->is_int());
bool enabled_for_arc =
ash::PrivacyHubController::CrosToArcGeolocationPermissionMapping(
static_cast<ash::GeolocationAccessLevel>(pref->GetValue()->GetInt()));
SendBoolValueSettingsBroadcast(
enabled_for_arc, !pref->IsUserModifiable(),
"org.chromium.arc.intent_helper.SET_USER_GEOLOCATION");
}
void ArcSettingsServiceImpl::SyncUserGeolocationAccuracy() const {
SendBoolPrefSettingsBroadcast(
ash::prefs::kUserGeolocationAccuracyEnabled,
"org.chromium.arc.intent_helper.SET_USER_GEOLOCATION_ACCURACY_ENABLED");
}
void ArcSettingsServiceImpl::SyncConsumerAutoUpdateToggle() const {
SendBoolLocalStatePrefSettingsBroadcast(
::prefs::kConsumerAutoUpdateToggle,
"org.chromium.arc.intent_helper.SET_CONSUMER_AUTO_UPDATE");
}
void ArcSettingsServiceImpl::ResetFontScaleToDefault() const {
base::Value::Dict extras;
extras.Set("scale", kAndroidFontScaleNormal);
SendSettingsBroadcast(kSetFontScaleAction, extras);
}
void ArcSettingsServiceImpl::ResetPageZoomToDefault() const {
base::Value::Dict extras;
extras.Set("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)));
}
void ArcSettingsServiceImpl::AddLocalStatePrefToObserve(
const std::string& pref_name) {
local_state_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::GetBooleanPref(
const std::string& pref_name) const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(pref_name);
DCHECK(pref);
DCHECK(pref->GetValue()->is_bool());
return pref->GetValue()->GetBool();
}
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::SendBoolLocalStatePrefSettingsBroadcast(
const std::string& pref_name,
const std::string& action) const {
DCHECK(g_browser_process);
const PrefService* local_state = g_browser_process->local_state();
DCHECK(local_state);
const PrefService::Preference* local_state_pref =
local_state->FindPreference(pref_name);
DCHECK(local_state_pref);
bool enabled = local_state->GetBoolean(pref_name);
SendBoolValueSettingsBroadcast(enabled, !local_state_pref->IsUserModifiable(),
action);
}
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::Value::Dict extras;
extras.Set("enabled", enabled);
extras.Set("managed", managed);
SendSettingsBroadcast(action, extras);
}
void ArcSettingsServiceImpl::SendSettingsBroadcast(
const std::string& action,
const base::Value::Dict& 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, kArcIntentHelperPackageName,
ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
"SettingsReceiver"),
extras_json);
}
void ArcSettingsServiceImpl::OnConnectionReady() {
arc_bridge_service_->app()->RemoveObserver(this);
SyncAppTimeSettings();
}
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);
}
void ArcSettingsService::EnsureFactoryBuilt() {
ArcSettingsServiceFactory::GetInstance();
}
}