Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(28)

Side by Side Diff: chrome/browser/prefs/pref_metrics_service.cc

Issue 22676002: Add UMA to report Preferences File Corruption (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Windows compile. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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/command_line.h"
9 #include "base/json/json_string_value_serializer.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 "base/strings/string_number_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/browser_shutdown.h"
16 // Accessing the Device ID API here is a layering violation.
17 // TODO(bbudge) Move the API so it's usable here.
18 // http://crbug.com/276485
19 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
20 #include "chrome/browser/extensions/extension_prefs.h"
10 #include "chrome/browser/prefs/pref_service_syncable.h" 21 #include "chrome/browser/prefs/pref_service_syncable.h"
22 #include "chrome/browser/prefs/scoped_user_pref_update.h"
11 #include "chrome/browser/prefs/session_startup_pref.h" 23 #include "chrome/browser/prefs/session_startup_pref.h"
12 #include "chrome/browser/prefs/synced_pref_change_registrar.h" 24 #include "chrome/browser/prefs/synced_pref_change_registrar.h"
13 #include "chrome/browser/profiles/incognito_helpers.h" 25 #include "chrome/browser/profiles/incognito_helpers.h"
14 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" 27 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
28 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/pref_names.h" 29 #include "chrome/common/pref_names.h"
17 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" 30 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h"
31 #include "crypto/hmac.h"
32 #include "grit/browser_resources.h"
18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 33 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
34 #include "ui/base/resource/resource_bundle.h"
19 35
20 namespace { 36 namespace {
21 37
22 // Converts a host name into a domain name for easier matching. 38 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
23 std::string GetDomainFromHost(const std::string& host) {
24 return net::registry_controlled_domains::GetDomainAndRegistry(
25 host,
26 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
27 }
28 39
29 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; 40 // These preferences must be kept in sync with the TrackedPreference enum in
41 // tools/metrics/histograms/histograms.xml. To add a new preference, append it
42 // to the array and add a corresponding value to the histogram enum.
43 const char* kTrackedPrefs[] = {
44 prefs::kShowHomeButton,
45 prefs::kHomePageIsNewTabPage,
46 prefs::kHomePage,
47 prefs::kRestoreOnStartup,
48 prefs::kURLsToRestoreOnStartup,
49 prefs::kExtensionsPref,
50 prefs::kGoogleServicesLastUsername,
51 prefs::kSearchProviderOverrides,
52 prefs::kDefaultSearchProviderSearchURL,
53 prefs::kDefaultSearchProviderKeyword,
54 prefs::kDefaultSearchProviderName,
55 prefs::kPinnedTabs,
56 };
57
58 static const size_t kSHA256DigestSize = 32;
30 59
31 } // namespace 60 } // namespace
32 61
33 PrefMetricsService::PrefMetricsService(Profile* profile) 62 PrefMetricsService::PrefMetricsService(Profile* profile)
34 : profile_(profile) { 63 : weak_factory_(this),
64 profile_(profile),
65 prefs_(profile_->GetPrefs()),
66 local_state_(g_browser_process->local_state()),
67 tracked_pref_paths_(kTrackedPrefs),
68 tracked_pref_path_count_(arraysize(kTrackedPrefs)) {
69 // Check that the pref is registered; otherwise some browser_tests crash.
70 if (prefs_->FindPreference(prefs::kGoogleServicesUsername))
71 profile_name_ = profile_->GetProfileName();
battre 2013/09/06 11:35:33 This should probably use profile_->GetPath() becau
bbudge 2013/09/06 13:31:34 Done.
72
73 pref_hash_seed_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
74 IDR_PREF_HASH_SEED_BIN).as_string();
75
35 RecordLaunchPrefs(); 76 RecordLaunchPrefs();
36 77
78 // Don't track preferences in browser tests. Otherwise, tests that change
79 // prefs after shutdown begins will write to local state, which will DCHECK
80 // when trying to write.
81 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
82 InitializePrefObservers();
83
84 #if !defined(OS_ANDROID)
85 // We need the machine id to compute pref value hashes. Fetch that, and then
86 // call CheckTrackedPreferences in the callback.
87 extensions::api::DeviceId::GetDeviceId(
battre 2013/09/06 11:35:33 This can deliver a synchronous callback at a time
bbudge 2013/09/06 13:31:34 I moved this to the end of the ctor, with a commen
88 "PrefMetricsService", // non-empty string to obfuscate the device id.
89 Bind(&PrefMetricsService::GetDeviceIdCallback,
90 weak_factory_.GetWeakPtr()));
91 #else
92 // Android has no GetDeviceId.
93 CheckTrackedPreferences();
94 #endif // !defined(OS_ANDROID)
95
37 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 96 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
38 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); 97 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs));
39 98
40 RegisterSyncedPrefObservers(); 99 RegisterSyncedPrefObservers();
41 } 100 }
42 101
102 // For unit testing only.
103 PrefMetricsService::PrefMetricsService(Profile* profile,
104 PrefService* local_state,
105 const std::string& device_id,
106 const char** tracked_pref_paths,
107 int tracked_pref_path_count)
108 : weak_factory_(this),
109 profile_(profile),
110 prefs_(profile->GetPrefs()),
111 local_state_(local_state),
112 pref_hash_seed_(kSHA256DigestSize, 0),
113 device_id_(device_id),
114 tracked_pref_paths_(tracked_pref_paths),
115 tracked_pref_path_count_(tracked_pref_path_count) {
116 CheckTrackedPreferences();
117 InitializePrefObservers();
118 }
119
43 PrefMetricsService::~PrefMetricsService() { 120 PrefMetricsService::~PrefMetricsService() {
44 } 121 }
45 122
46 void PrefMetricsService::RecordLaunchPrefs() { 123 void PrefMetricsService::RecordLaunchPrefs() {
47 PrefService* prefs = profile_->GetPrefs(); 124 bool show_home_button = prefs_->GetBoolean(prefs::kShowHomeButton);
48 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); 125 bool home_page_is_ntp = prefs_->GetBoolean(prefs::kHomePageIsNewTabPage);
49 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage);
50 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); 126 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button);
51 if (show_home_button) { 127 if (show_home_button) {
52 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", 128 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage",
53 home_page_is_ntp); 129 home_page_is_ntp);
54 } 130 }
55 131
56 // For non-NTP homepages, see if the URL comes from the same TLD+1 as a known 132 // For non-NTP homepages, see if the URL comes from the same TLD+1 as a known
57 // search engine. Note that this is only an approximation of search engine 133 // search engine. Note that this is only an approximation of search engine
58 // use, due to both false negatives (pages that come from unknown TLD+1 X but 134 // use, due to both false negatives (pages that come from unknown TLD+1 X but
59 // consist of a search box that sends to known TLD+1 Y) and false positives 135 // consist of a search box that sends to known TLD+1 Y) and false positives
60 // (pages that share a TLD+1 with a known engine but aren't actually search 136 // (pages that share a TLD+1 with a known engine but aren't actually search
61 // pages, e.g. plus.google.com). 137 // pages, e.g. plus.google.com).
62 if (!home_page_is_ntp) { 138 if (!home_page_is_ntp) {
63 GURL homepage_url(prefs->GetString(prefs::kHomePage)); 139 GURL homepage_url(prefs_->GetString(prefs::kHomePage));
64 if (homepage_url.is_valid()) { 140 if (homepage_url.is_valid()) {
65 UMA_HISTOGRAM_ENUMERATION( 141 UMA_HISTOGRAM_ENUMERATION(
66 "Settings.HomePageEngineType", 142 "Settings.HomePageEngineType",
67 TemplateURLPrepopulateData::GetEngineType(homepage_url), 143 TemplateURLPrepopulateData::GetEngineType(homepage_url),
68 SEARCH_ENGINE_MAX); 144 SEARCH_ENGINE_MAX);
69 } 145 }
70 } 146 }
71 147
72 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); 148 int restore_on_startup = prefs_->GetInteger(prefs::kRestoreOnStartup);
73 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", 149 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings",
74 restore_on_startup, kSessionStartupPrefValueMax); 150 restore_on_startup, kSessionStartupPrefValueMax);
75 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { 151 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) {
76 const ListValue* url_list = prefs->GetList(prefs::kURLsToRestoreOnStartup); 152 const ListValue* url_list = prefs_->GetList(prefs::kURLsToRestoreOnStartup);
77 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.StartupPageLoadURLs", 153 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.StartupPageLoadURLs",
78 url_list->GetSize(), 1, 50, 20); 154 url_list->GetSize(), 1, 50, 20);
79 // Similarly, check startup pages for known search engine TLD+1s. 155 // Similarly, check startup pages for known search engine TLD+1s.
80 std::string url_text; 156 std::string url_text;
81 for (size_t i = 0; i < url_list->GetSize(); i++) { 157 for (size_t i = 0; i < url_list->GetSize(); i++) {
82 if (url_list->GetString(i, &url_text)) { 158 if (url_list->GetString(i, &url_text)) {
83 GURL start_url(url_text); 159 GURL start_url(url_text);
84 if (start_url.is_valid()) { 160 if (start_url.is_valid()) {
85 UMA_HISTOGRAM_ENUMERATION( 161 UMA_HISTOGRAM_ENUMERATION(
86 "Settings.StartupPageEngineTypes", 162 "Settings.StartupPageEngineTypes",
87 TemplateURLPrepopulateData::GetEngineType(start_url), 163 TemplateURLPrepopulateData::GetEngineType(start_url),
88 SEARCH_ENGINE_MAX); 164 SEARCH_ENGINE_MAX);
89 } 165 }
90 } 166 }
91 } 167 }
92 } 168 }
93 } 169 }
94 170
171 // static
172 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
173 // Register the top level dictionary to map profile names to dictionaries of
174 // tracked preferences.
175 registry->RegisterDictionaryPref(prefs::kProfilePreferenceHashes);
176 }
177
95 void PrefMetricsService::RegisterSyncedPrefObservers() { 178 void PrefMetricsService::RegisterSyncedPrefObservers() {
96 LogHistogramValueCallback booleanHandler = base::Bind( 179 LogHistogramValueCallback booleanHandler = base::Bind(
97 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); 180 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this));
98 181
99 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); 182 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler);
100 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", 183 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage",
101 booleanHandler); 184 booleanHandler);
102 185
103 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", 186 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings",
104 base::Bind(&PrefMetricsService::LogIntegerPrefChange, 187 base::Bind(&PrefMetricsService::LogIntegerPrefChange,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 const ListValue* items = NULL; 245 const ListValue* items = NULL;
163 if (!value->GetAsList(&items)) 246 if (!value->GetAsList(&items))
164 return; 247 return;
165 for (size_t i = 0; i < items->GetSize(); ++i) { 248 for (size_t i = 0; i < items->GetSize(); ++i) {
166 const Value *item_value = NULL; 249 const Value *item_value = NULL;
167 if (items->Get(i, &item_value)) 250 if (items->Get(i, &item_value))
168 item_callback.Run(histogram_name, item_value); 251 item_callback.Run(histogram_name, item_value);
169 } 252 }
170 } 253 }
171 254
255 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) {
256 device_id_ = device_id;
257 CheckTrackedPreferences();
258 }
259
260 // To detect changes to Preferences that happen outside of Chrome, we hash
261 // selected pref values and save them in local state. CheckTrackedPreferences
262 // compares the saved values to the values observed in the profile's prefs. A
263 // dictionary of dictionaries in local state holds the hashed values, grouped by
264 // profile. To make the system more resistant to spoofing, pref values are
265 // hashed with the pref path and the device id.
266 void PrefMetricsService::CheckTrackedPreferences() {
267 const base::DictionaryValue* pref_hash_dicts =
268 local_state_->GetDictionary(prefs::kProfilePreferenceHashes);
269 // Get the hashed prefs dictionary if it exists. If it doesn't, it will be
270 // created if we set preference values below.
271 const base::DictionaryValue* hashed_prefs = NULL;
272 pref_hash_dicts->GetDictionary(profile_name_, &hashed_prefs);
273 for (int i = 0; i < tracked_pref_path_count_; ++i) {
274 // Skip prefs that haven't been registered (e.g. in browser_tests).
275 if (!prefs_->FindPreference(tracked_pref_paths_[i]))
276 continue;
277
278 bool changed = false;
279 const base::Value* value = prefs_->GetUserPrefValue(tracked_pref_paths_[i]);
280 if (value) {
281 std::string value_hash =
282 GetHashedPrefValue(tracked_pref_paths_[i], value);
283 std::string last_hash;
284 if (hashed_prefs &&
285 hashed_prefs->GetString(tracked_pref_paths_[i], &last_hash)) {
286 if (value_hash != last_hash) {
287 changed = true;
288 // Record that the preference changed from its last value.
289 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged",
290 i, tracked_pref_path_count_);
291 UpdateTrackedPreference(tracked_pref_paths_[i]);
292 }
293 } else {
294 changed = true;
295 // Record that we haven't tracked this preference yet, or the hash in
296 // local state was removed.
297 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized",
298 i, tracked_pref_path_count_);
299 UpdateTrackedPreference(tracked_pref_paths_[i]);
300 }
301 } else {
302 // There is no preference set. Remove any hashed value from local state
303 // and if one was present, record that a pref was cleared.
304 if (RemoveTrackedPreference(tracked_pref_paths_[i])) {
305 changed = true;
306 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceCleared",
307 i, tracked_pref_path_count_);
308 }
309 }
310 if (!changed) {
311 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceUnchanged",
312 i, tracked_pref_path_count_);
313 }
314 }
315 }
316
317 void PrefMetricsService::UpdateTrackedPreference(const char* path) {
318 const base::Value* value = prefs_->GetUserPrefValue(path);
319 if (value) {
320 DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
321 base::DictionaryValue* dict = update.Get();
322 if (dict) {
323 dict->SetString(GetHashedPrefPath(path),
324 GetHashedPrefValue(path, value));
325 }
battre 2013/09/06 11:35:33 A value is created on demand by DictionaryPrefUpda
bbudge 2013/09/06 13:31:34 Nice. Done.
326 } else {
327 RemoveTrackedPreference(path);
328 }
329 }
330
331 bool PrefMetricsService::RemoveTrackedPreference(const char* path) {
332 DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
333 base::DictionaryValue* dict = update.Get();
334 if (dict)
335 return dict->Remove(GetHashedPrefPath(path), NULL);
336
337 return false;
338 }
339
340 std::string PrefMetricsService::GetHashedPrefPath(const char* path) {
341 std::string hash_pref_path(profile_name_);
342 hash_pref_path.append(".");
343 hash_pref_path.append(path);
344 return hash_pref_path;
345 }
346
347 std::string PrefMetricsService::GetHashedPrefValue(
348 const char* path,
349 const base::Value* value) {
350 DCHECK(value);
351
352 std::string string_to_hash(device_id_);
353 string_to_hash.append(path);
354 JSONStringValueSerializer serializer(&string_to_hash);
355 serializer.Serialize(*value);
356
357 crypto::HMAC hmac(crypto::HMAC::SHA256);
358 unsigned char digest[kSHA256DigestSize];
359 if (!hmac.Init(pref_hash_seed_) ||
360 !hmac.Sign(string_to_hash, digest, kSHA256DigestSize)) {
361 NOTREACHED();
362 return std::string();
363 }
364
365 return base::HexEncode(digest, kSHA256DigestSize);
366 }
367
368 void PrefMetricsService::InitializePrefObservers() {
369 pref_registrar_.Init(prefs_);
370 for (int i = 0; i < tracked_pref_path_count_; ++i) {
371 pref_registrar_.Add(
372 tracked_pref_paths_[i],
373 base::Bind(&PrefMetricsService::UpdateTrackedPreference,
374 weak_factory_.GetWeakPtr(),
375 tracked_pref_paths_[i]));
376 }
377 }
378
172 // static 379 // static
173 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { 380 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() {
174 return Singleton<PrefMetricsService::Factory>::get(); 381 return Singleton<PrefMetricsService::Factory>::get();
175 } 382 }
176 383
177 // static 384 // static
178 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( 385 PrefMetricsService* PrefMetricsService::Factory::GetForProfile(
179 Profile* profile) { 386 Profile* profile) {
180 return static_cast<PrefMetricsService*>( 387 return static_cast<PrefMetricsService*>(
181 GetInstance()->GetServiceForBrowserContext(profile, true)); 388 GetInstance()->GetServiceForBrowserContext(profile, true));
(...skipping 19 matching lines...) Expand all
201 } 408 }
202 409
203 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { 410 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const {
204 return false; 411 return false;
205 } 412 }
206 413
207 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( 414 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse(
208 content::BrowserContext* context) const { 415 content::BrowserContext* context) const {
209 return chrome::GetBrowserContextRedirectedInIncognito(context); 416 return chrome::GetBrowserContextRedirectedInIncognito(context);
210 } 417 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698