blob: 74dd19cc5bf713b9ceb36466204fff70ca1cbaa7 [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/chromeos/arc/arc_settings_service.h"
#include <string>
#include "base/gtest_prod_util.h"
#include "base/json/json_writer.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/timezone_settings.h"
#include "components/arc/intent_helper/font_size_util.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "net/proxy/proxy_config.h"
using ::chromeos::CrosSettings;
using ::chromeos::system::TimezoneSettings;
namespace {
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_SINGLE_PROXY) {
proxy_list = &proxy_rules.single_proxies;
} else if (proxy_rules.type ==
net::ProxyConfig::ProxyRules::TYPE_PROXY_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;
}
} // namespace
namespace arc {
// 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 chromeos::system::TimezoneSettings::Observer,
public device::BluetoothAdapter::Observer {
public:
explicit ArcSettingsServiceImpl(ArcBridgeService* arc_bridge_service);
~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;
// BluetoothAdapter::Observer
void AdapterPoweredChanged(device::BluetoothAdapter* adapter,
bool powered) override;
private:
// Registers to observe changes for Chrome settings we care about.
void StartObservingSettingsChanges();
// Stops listening for Chrome settings changes.
void StopObservingSettingsChanges();
// Retrives Chrome's state for the settings and send it to Android.
void SyncAllPrefs() const;
void SyncFontSize() const;
void SyncLocale() const;
void SyncProxySettings() const;
void SyncReportingConsent() const;
void SyncSpokenFeedbackEnabled() const;
void SyncTimeZone() const;
void SyncUse24HourClock() const;
void OnBluetoothAdapterInitialized(
scoped_refptr<device::BluetoothAdapter> adapter);
// 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;
// Sends a broadcast to the delegate.
void SendSettingsBroadcast(const std::string& action,
const base::DictionaryValue& extras) const;
// Manages pref observation registration.
PrefChangeRegistrar registrar_;
std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
reporting_consent_subscription_;
ArcBridgeService* const arc_bridge_service_;
scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
// WeakPtrFactory to use for callback for getting the bluetooth adapter.
base::WeakPtrFactory<ArcSettingsServiceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ArcSettingsServiceImpl);
};
ArcSettingsServiceImpl::ArcSettingsServiceImpl(
ArcBridgeService* arc_bridge_service)
: arc_bridge_service_(arc_bridge_service), weak_factory_(this) {
StartObservingSettingsChanges();
SyncAllPrefs();
}
ArcSettingsServiceImpl::~ArcSettingsServiceImpl() {
StopObservingSettingsChanges();
if (bluetooth_adapter_) {
bluetooth_adapter_->RemoveObserver(this);
}
}
void ArcSettingsServiceImpl::StartObservingSettingsChanges() {
Profile* profile = ProfileManager::GetActiveUserProfile();
registrar_.Init(profile->GetPrefs());
AddPrefToObserve(prefs::kWebKitDefaultFixedFontSize);
AddPrefToObserve(prefs::kWebKitDefaultFontSize);
AddPrefToObserve(prefs::kWebKitMinimumFontSize);
AddPrefToObserve(prefs::kAccessibilitySpokenFeedbackEnabled);
AddPrefToObserve(prefs::kUse24HourClock);
AddPrefToObserve(proxy_config::prefs::kProxy);
reporting_consent_subscription_ = CrosSettings::Get()->AddSettingsObserver(
chromeos::kStatsReportingPref,
base::Bind(&ArcSettingsServiceImpl::SyncReportingConsent,
base::Unretained(this)));
TimezoneSettings::GetInstance()->AddObserver(this);
if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&ArcSettingsServiceImpl::OnBluetoothAdapterInitialized,
weak_factory_.GetWeakPtr()));
}
}
void ArcSettingsServiceImpl::OnBluetoothAdapterInitialized(
scoped_refptr<device::BluetoothAdapter> adapter) {
DCHECK(adapter);
bluetooth_adapter_ = adapter;
bluetooth_adapter_->AddObserver(this);
AdapterPoweredChanged(adapter.get(), adapter->IsPowered());
}
void ArcSettingsServiceImpl::SyncAllPrefs() const {
SyncFontSize();
SyncLocale();
SyncProxySettings();
SyncReportingConsent();
SyncSpokenFeedbackEnabled();
SyncTimeZone();
SyncUse24HourClock();
}
void ArcSettingsServiceImpl::StopObservingSettingsChanges() {
registrar_.RemoveAll();
reporting_consent_subscription_.reset();
TimezoneSettings::GetInstance()->RemoveObserver(this);
}
void ArcSettingsServiceImpl::AddPrefToObserve(const std::string& pref_name) {
registrar_.Add(pref_name, base::Bind(&ArcSettingsServiceImpl::OnPrefChanged,
base::Unretained(this)));
}
void ArcSettingsServiceImpl::AdapterPoweredChanged(
device::BluetoothAdapter* adapter,
bool powered) {
base::DictionaryValue extras;
extras.SetBoolean("enable", powered);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_BLUETOOTH_STATE",
extras);
}
void ArcSettingsServiceImpl::OnPrefChanged(const std::string& pref_name) const {
if (pref_name == prefs::kAccessibilitySpokenFeedbackEnabled) {
SyncSpokenFeedbackEnabled();
} else if (pref_name == prefs::kWebKitDefaultFixedFontSize ||
pref_name == prefs::kWebKitDefaultFontSize ||
pref_name == prefs::kWebKitMinimumFontSize) {
SyncFontSize();
} else if (pref_name == prefs::kUse24HourClock) {
SyncUse24HourClock();
} else if (pref_name == proxy_config::prefs::kProxy) {
SyncProxySettings();
} else {
LOG(ERROR) << "Unknown pref changed.";
}
}
void ArcSettingsServiceImpl::TimezoneChanged(const icu::TimeZone& timezone) {
SyncTimeZone();
}
int ArcSettingsServiceImpl::GetIntegerPref(const std::string& pref_name) const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(pref_name);
DCHECK(pref);
int val = -1;
bool value_exists = pref->GetValue()->GetAsInteger(&val);
DCHECK(value_exists);
return val;
}
void ArcSettingsServiceImpl::SyncFontSize() const {
int default_size = GetIntegerPref(prefs::kWebKitDefaultFontSize);
int default_fixed_size = GetIntegerPref(prefs::kWebKitDefaultFixedFontSize);
int minimum_size = GetIntegerPref(prefs::kWebKitMinimumFontSize);
double android_scale = ConvertFontSizeChromeToAndroid(
default_size, default_fixed_size, minimum_size);
base::DictionaryValue extras;
extras.SetDouble("scale", android_scale);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_FONT_SCALE",
extras);
}
void ArcSettingsServiceImpl::SyncSpokenFeedbackEnabled() const {
const PrefService::Preference* pref = registrar_.prefs()->FindPreference(
prefs::kAccessibilitySpokenFeedbackEnabled);
DCHECK(pref);
bool enabled = false;
bool value_exists = pref->GetValue()->GetAsBoolean(&enabled);
DCHECK(value_exists);
base::DictionaryValue extras;
extras.SetBoolean("enabled", enabled);
SendSettingsBroadcast(
"org.chromium.arc.intent_helper.SET_SPOKEN_FEEDBACK_ENABLED", extras);
}
void ArcSettingsServiceImpl::SyncLocale() const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(prefs::kApplicationLocale);
DCHECK(pref);
std::string locale;
bool value_exists = pref->GetValue()->GetAsString(&locale);
DCHECK(value_exists);
base::DictionaryValue extras;
extras.SetString("locale", locale);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_LOCALE", extras);
}
void ArcSettingsServiceImpl::SyncReportingConsent() const {
bool consent = false;
CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref, &consent);
base::DictionaryValue extras;
extras.SetBoolean("reportingConsent", consent);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_REPORTING_CONSENT",
extras);
}
void ArcSettingsServiceImpl::SyncTimeZone() const {
TimezoneSettings* timezone_settings = TimezoneSettings::GetInstance();
base::string16 timezoneID = timezone_settings->GetCurrentTimezoneID();
base::DictionaryValue extras;
extras.SetString("olsonTimeZone", timezoneID);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_TIME_ZONE", extras);
}
void ArcSettingsServiceImpl::SyncUse24HourClock() const {
const PrefService::Preference* pref =
registrar_.prefs()->FindPreference(prefs::kUse24HourClock);
DCHECK(pref);
bool use24HourClock = false;
bool value_exists = pref->GetValue()->GetAsBoolean(&use24HourClock);
DCHECK(value_exists);
base::DictionaryValue extras;
extras.SetBoolean("use24HourClock", use24HourClock);
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_USE_24_HOUR_CLOCK",
extras);
}
void ArcSettingsServiceImpl::SyncProxySettings() const {
const PrefService::Preference* const pref =
registrar_.prefs()->FindPreference(proxy_config::prefs::kProxy);
const base::DictionaryValue* proxy_config_value;
bool value_exists = pref->GetValue()->GetAsDictionary(&proxy_config_value);
DCHECK(value_exists);
ProxyConfigDictionary proxy_config_dict(proxy_config_value);
ProxyPrefs::ProxyMode mode;
if (!proxy_config_dict.GetMode(&mode))
mode = ProxyPrefs::MODE_DIRECT;
base::DictionaryValue extras;
extras.SetString("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:
extras.SetString("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.SetString("pacUrl", pac_url);
break;
}
case ProxyPrefs::MODE_FIXED_SERVERS: {
std::string host;
int port = 0;
if (!GetHttpProxyServer(proxy_config_dict, &host, &port)) {
LOG(ERROR) << "No Http proxy server is sent.";
return;
}
extras.SetString("host", host);
extras.SetInteger("port", port);
std::string bypass_list;
if (proxy_config_dict.GetBypassList(&bypass_list) &&
!bypass_list.empty()) {
extras.SetString("bypassList", bypass_list);
}
break;
}
default:
LOG(ERROR) << "Incorrect proxy mode.";
return;
}
SendSettingsBroadcast("org.chromium.arc.intent_helper.SET_PROXY", extras);
}
void ArcSettingsServiceImpl::SendSettingsBroadcast(
const std::string& action,
const base::DictionaryValue& extras) const {
if (!arc_bridge_service_->intent_helper()->instance()) {
LOG(ERROR) << "IntentHelper instance is not ready.";
return;
}
std::string extras_json;
bool write_success = base::JSONWriter::Write(extras, &extras_json);
DCHECK(write_success);
if (arc_bridge_service_->intent_helper()->version() >= 1) {
arc_bridge_service_->intent_helper()->instance()->SendBroadcast(
action, "org.chromium.arc.intent_helper",
"org.chromium.arc.intent_helper.SettingsReceiver", extras_json);
}
}
ArcSettingsService::ArcSettingsService(ArcBridgeService* bridge_service)
: ArcService(bridge_service) {
arc_bridge_service()->intent_helper()->AddObserver(this);
}
ArcSettingsService::~ArcSettingsService() {
arc_bridge_service()->intent_helper()->RemoveObserver(this);
}
void ArcSettingsService::OnInstanceReady() {
impl_.reset(new ArcSettingsServiceImpl(arc_bridge_service()));
}
void ArcSettingsService::OnInstanceClosed() {
impl_.reset();
}
} // namespace arc