OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/prefs/pref_metrics_service.h" | 5 #include "chrome/browser/prefs/pref_metrics_service.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/json/json_string_value_serializer.h" |
| 9 #include "base/md5.h" |
8 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/prefs/pref_registry_simple.h" |
9 #include "base/prefs/pref_service.h" | 12 #include "base/prefs/pref_service.h" |
| 13 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/browser_shutdown.h" |
| 15 #include "chrome/browser/extensions/api/music_manager_private/device_id.h" |
| 16 #include "chrome/browser/extensions/extension_prefs.h" |
10 #include "chrome/browser/prefs/pref_service_syncable.h" | 17 #include "chrome/browser/prefs/pref_service_syncable.h" |
| 18 #include "chrome/browser/prefs/scoped_user_pref_update.h" |
11 #include "chrome/browser/prefs/session_startup_pref.h" | 19 #include "chrome/browser/prefs/session_startup_pref.h" |
12 #include "chrome/browser/prefs/synced_pref_change_registrar.h" | 20 #include "chrome/browser/prefs/synced_pref_change_registrar.h" |
13 #include "chrome/browser/profiles/incognito_helpers.h" | 21 #include "chrome/browser/profiles/incognito_helpers.h" |
14 #include "chrome/browser/profiles/profile.h" | 22 #include "chrome/browser/profiles/profile.h" |
15 #include "chrome/common/pref_names.h" | 23 #include "chrome/common/pref_names.h" |
16 #include "components/browser_context_keyed_service/browser_context_dependency_ma
nager.h" | 24 #include "components/browser_context_keyed_service/browser_context_dependency_ma
nager.h" |
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 25 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
18 | 26 |
19 namespace { | 27 namespace { |
20 | 28 |
21 // Converts a host name into a domain name for easier matching. | 29 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; |
22 std::string GetDomainFromHost(const std::string& host) { | 30 |
23 return net::registry_controlled_domains::GetDomainAndRegistry( | 31 // In order to detect changes to Preferences that happen outside of Chrome, we |
24 host, | 32 // save selected prefs in local state. When Chrome is started up, we compare |
25 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | 33 // the saved values against the values observed in the user's Preferences file. |
| 34 // We add a dictionary of dictionaries to local state to hold the saved values, |
| 35 // grouped by profile. To make the system more resistant to spoofing, each |
| 36 // preference value is hashed with its path and the device id. |
| 37 const char kProfilePreferenceHashes[] = "profile.preference_hashes"; |
| 38 |
| 39 // These preferences must be kept in sync with the TrackedPreference enum in |
| 40 // tools/metrics/histograms/histograms.xml. To add a new preference, append it |
| 41 // to the array and add a corresponding value to the histogram enum. |
| 42 const char* kTrackedPrefs[] = { |
| 43 prefs::kShowHomeButton, |
| 44 prefs::kHomePageIsNewTabPage, |
| 45 prefs::kHomePage, |
| 46 prefs::kRestoreOnStartup, |
| 47 prefs::kURLsToRestoreOnStartup, |
| 48 extensions::ExtensionPrefs::kExtensionsPref, |
| 49 }; |
| 50 |
| 51 bool BrowserShuttingDown() { |
| 52 return browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID; |
26 } | 53 } |
27 | 54 |
28 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; | |
29 | |
30 } // namespace | 55 } // namespace |
31 | 56 |
32 PrefMetricsService::PrefMetricsService(Profile* profile) | 57 PrefMetricsService::PrefMetricsService(Profile* profile) |
33 : profile_(profile) { | 58 : weak_factory_(this), |
| 59 profile_(profile), |
| 60 prefs_(profile_->GetPrefs()), |
| 61 local_state_(g_browser_process->local_state()), |
| 62 tracked_pref_paths_(kTrackedPrefs), |
| 63 tracked_pref_path_count_(arraysize(kTrackedPrefs)) { |
| 64 // Get the profile name (some browser tests don't set this up, in which |
| 65 // case profile_name_ is the empty string). |
| 66 if (prefs_->FindPreference(prefs::kGoogleServicesUsername)) |
| 67 profile_name_ = profile_->GetProfileName(); |
| 68 |
34 RecordLaunchPrefs(); | 69 RecordLaunchPrefs(); |
35 | 70 |
| 71 #if !defined(OS_ANDROID) |
| 72 // We need the machine id to compute pref value hashes. Fetch that, and then |
| 73 // call CheckTrackedPreferences in the callback. |
| 74 extensions::api::DeviceId::GetDeviceId( |
| 75 "PrefMetricsService", // non-empty string to obfuscate the device id. |
| 76 Bind(&PrefMetricsService::GetDeviceIdCallback, |
| 77 weak_factory_.GetWeakPtr())); |
| 78 #else |
| 79 // Android has no GetDeviceId. |
| 80 CheckTrackedPreferences(); |
| 81 #endif // !defined(OS_ANDROID) |
| 82 |
| 83 InitializePrefObservers(); |
| 84 |
36 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); | 85 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); |
37 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); | 86 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); |
38 | 87 |
39 RegisterSyncedPrefObservers(); | 88 RegisterSyncedPrefObservers(); |
40 } | 89 } |
41 | 90 |
| 91 PrefMetricsService::PrefMetricsService(Profile* profile, |
| 92 PrefService* local_state, |
| 93 const std::string& device_id, |
| 94 const char** tracked_pref_paths, |
| 95 int tracked_pref_path_count) |
| 96 : weak_factory_(this), |
| 97 profile_(profile), |
| 98 prefs_(profile->GetPrefs()), |
| 99 local_state_(local_state), |
| 100 device_id_(device_id), |
| 101 tracked_pref_paths_(tracked_pref_paths), |
| 102 tracked_pref_path_count_(tracked_pref_path_count) { |
| 103 CheckTrackedPreferences(); |
| 104 InitializePrefObservers(); |
| 105 } |
| 106 |
42 PrefMetricsService::~PrefMetricsService() { | 107 PrefMetricsService::~PrefMetricsService() { |
43 } | 108 } |
44 | 109 |
45 void PrefMetricsService::RecordLaunchPrefs() { | 110 void PrefMetricsService::RecordLaunchPrefs() { |
46 PrefService* prefs = profile_->GetPrefs(); | 111 bool show_home_button = prefs_->GetBoolean(prefs::kShowHomeButton); |
47 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); | 112 bool home_page_is_ntp = prefs_->GetBoolean(prefs::kHomePageIsNewTabPage); |
48 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); | |
49 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); | 113 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); |
50 if (show_home_button) { | 114 if (show_home_button) { |
51 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", | 115 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", |
52 home_page_is_ntp); | 116 home_page_is_ntp); |
53 } | 117 } |
54 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); | 118 int restore_on_startup = prefs_->GetInteger(prefs::kRestoreOnStartup); |
55 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", | 119 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", |
56 restore_on_startup, kSessionStartupPrefValueMax); | 120 restore_on_startup, kSessionStartupPrefValueMax); |
57 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { | 121 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { |
58 const int url_list_size = prefs->GetList( | 122 const int url_list_size = prefs_->GetList( |
59 prefs::kURLsToRestoreOnStartup)->GetSize(); | 123 prefs::kURLsToRestoreOnStartup)->GetSize(); |
60 UMA_HISTOGRAM_CUSTOM_COUNTS( | 124 UMA_HISTOGRAM_CUSTOM_COUNTS( |
61 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); | 125 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); |
62 } | 126 } |
63 } | 127 } |
64 | 128 |
| 129 // static |
| 130 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) { |
| 131 // Register the top level dictionary to map profile names to dictionaries of |
| 132 // tracked preferences. |
| 133 registry->RegisterDictionaryPref(kProfilePreferenceHashes); |
| 134 } |
| 135 |
65 void PrefMetricsService::RegisterSyncedPrefObservers() { | 136 void PrefMetricsService::RegisterSyncedPrefObservers() { |
66 LogHistogramValueCallback booleanHandler = base::Bind( | 137 LogHistogramValueCallback booleanHandler = base::Bind( |
67 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); | 138 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); |
68 | 139 |
69 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); | 140 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); |
70 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", | 141 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", |
71 booleanHandler); | 142 booleanHandler); |
72 | 143 |
73 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", | 144 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", |
74 base::Bind(&PrefMetricsService::LogIntegerPrefChange, | 145 base::Bind(&PrefMetricsService::LogIntegerPrefChange, |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 const ListValue* items = NULL; | 203 const ListValue* items = NULL; |
133 if (!value->GetAsList(&items)) | 204 if (!value->GetAsList(&items)) |
134 return; | 205 return; |
135 for (size_t i = 0; i < items->GetSize(); ++i) { | 206 for (size_t i = 0; i < items->GetSize(); ++i) { |
136 const Value *item_value = NULL; | 207 const Value *item_value = NULL; |
137 if (items->Get(i, &item_value)) | 208 if (items->Get(i, &item_value)) |
138 item_callback.Run(histogram_name, item_value); | 209 item_callback.Run(histogram_name, item_value); |
139 } | 210 } |
140 } | 211 } |
141 | 212 |
| 213 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) { |
| 214 device_id_ = device_id; |
| 215 CheckTrackedPreferences(); |
| 216 } |
| 217 |
| 218 void PrefMetricsService::CheckTrackedPreferences() { |
| 219 UMA_HISTOGRAM_BOOLEAN("Settings.TrackedPreferencesChecked", true); |
| 220 |
| 221 const base::DictionaryValue* pref_hash_dicts = |
| 222 local_state_->GetDictionary(kProfilePreferenceHashes); |
| 223 |
| 224 // Get the hashed prefs dictionary if it exists. If it doesn't, it will be |
| 225 // created if we set preference values below. |
| 226 const base::DictionaryValue* hashed_prefs = NULL; |
| 227 pref_hash_dicts->GetDictionary(profile_name_, &hashed_prefs); |
| 228 for (int i = 0; i < tracked_pref_path_count_; ++i) { |
| 229 const base::Value* value = prefs_->GetUserPrefValue(tracked_pref_paths_[i]); |
| 230 if (value) { |
| 231 std::string value_hash = |
| 232 GetHashedPrefValue(tracked_pref_paths_[i], value); |
| 233 std::string last_hash; |
| 234 if (hashed_prefs && |
| 235 hashed_prefs->GetString(tracked_pref_paths_[i], &last_hash)) { |
| 236 if (value_hash != last_hash) { |
| 237 // Record that the preference changed from its last value. |
| 238 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged", |
| 239 i, tracked_pref_path_count_); |
| 240 UpdateTrackedPreference(tracked_pref_paths_[i]); |
| 241 } |
| 242 } else { |
| 243 // Record that we haven't tracked this preference yet, or the hash in |
| 244 // local state was removed. |
| 245 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized", |
| 246 i, tracked_pref_path_count_); |
| 247 UpdateTrackedPreference(tracked_pref_paths_[i]); |
| 248 } |
| 249 } else { |
| 250 // There is no preference set. Remove any hashed value from local state |
| 251 // and if one was present, record that a pref was removed. |
| 252 if (RemoveTrackedPreference(tracked_pref_paths_[i])) { |
| 253 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceRemoved", |
| 254 i, tracked_pref_path_count_); |
| 255 } |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 void PrefMetricsService::UpdateTrackedPreference(const char* path) { |
| 261 // Don't try to change local state during browser shutdown. |
| 262 if (BrowserShuttingDown()) |
| 263 return; |
| 264 |
| 265 const base::Value* value = prefs_->GetUserPrefValue(path); |
| 266 if (value) { |
| 267 DictionaryPrefUpdate update(local_state_, kProfilePreferenceHashes); |
| 268 base::DictionaryValue* prefs = update.Get(); |
| 269 if (prefs) { |
| 270 prefs->SetString(GetHashedPrefPath(path), |
| 271 GetHashedPrefValue(path, value)); |
| 272 } |
| 273 } else { |
| 274 RemoveTrackedPreference(path); |
| 275 } |
| 276 } |
| 277 |
| 278 bool PrefMetricsService::RemoveTrackedPreference(const char* path) { |
| 279 // Don't try to change local state during browser shutdown. |
| 280 if (BrowserShuttingDown()) |
| 281 return false; |
| 282 |
| 283 DictionaryPrefUpdate update(local_state_, kProfilePreferenceHashes); |
| 284 base::DictionaryValue* prefs = update.Get(); |
| 285 if (prefs) |
| 286 return prefs->Remove(GetHashedPrefPath(path), NULL); |
| 287 |
| 288 return false; |
| 289 } |
| 290 |
| 291 std::string PrefMetricsService::GetHashedPrefPath(const char* path) { |
| 292 std::string hash_pref_path(profile_name_); |
| 293 hash_pref_path.append("."); |
| 294 hash_pref_path.append(path); |
| 295 return hash_pref_path; |
| 296 } |
| 297 |
| 298 std::string PrefMetricsService::GetHashedPrefValue( |
| 299 const char* path, |
| 300 const base::Value* value) { |
| 301 DCHECK(value); |
| 302 std::string value_string; |
| 303 JSONStringValueSerializer serializer(&value_string); |
| 304 serializer.Serialize(*value); |
| 305 |
| 306 base::MD5Context context; |
| 307 base::MD5Init(&context); |
| 308 base::MD5Update(&context, device_id_); // Salt with device id. |
| 309 base::MD5Update(&context, path); // Salt with pref path. |
| 310 base::MD5Update(&context, value_string); |
| 311 base::MD5Digest digest; |
| 312 base::MD5Final(&digest, &context); |
| 313 return base::MD5DigestToBase16(digest); |
| 314 } |
| 315 |
| 316 void PrefMetricsService::InitializePrefObservers() { |
| 317 // It's tricky to save preference value hashes on browser shutdown. Instead, |
| 318 // register for notifications when tracked preferences change so we can update |
| 319 // the hashes in local state. |
| 320 pref_registrar_.Init(prefs_); |
| 321 for (int i = 0; i < tracked_pref_path_count_; ++i) { |
| 322 pref_registrar_.Add( |
| 323 tracked_pref_paths_[i], |
| 324 base::Bind(&PrefMetricsService::UpdateTrackedPreference, |
| 325 weak_factory_.GetWeakPtr(), |
| 326 tracked_pref_paths_[i])); |
| 327 } |
| 328 } |
| 329 |
142 // static | 330 // static |
143 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { | 331 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { |
144 return Singleton<PrefMetricsService::Factory>::get(); | 332 return Singleton<PrefMetricsService::Factory>::get(); |
145 } | 333 } |
146 | 334 |
147 // static | 335 // static |
148 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( | 336 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( |
149 Profile* profile) { | 337 Profile* profile) { |
150 return static_cast<PrefMetricsService*>( | 338 return static_cast<PrefMetricsService*>( |
151 GetInstance()->GetServiceForBrowserContext(profile, true)); | 339 GetInstance()->GetServiceForBrowserContext(profile, true)); |
(...skipping 19 matching lines...) Expand all Loading... |
171 } | 359 } |
172 | 360 |
173 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { | 361 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { |
174 return false; | 362 return false; |
175 } | 363 } |
176 | 364 |
177 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( | 365 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( |
178 content::BrowserContext* context) const { | 366 content::BrowserContext* context) const { |
179 return chrome::GetBrowserContextRedirectedInIncognito(context); | 367 return chrome::GetBrowserContextRedirectedInIncognito(context); |
180 } | 368 } |
OLD | NEW |