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

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: Using hash seed resource. 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"
10 #include "base/md5.h"
8 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_registry_simple.h"
9 #include "base/prefs/pref_service.h" 13 #include "base/prefs/pref_service.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 "grit/browser_resources.h"
18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 32 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
33 #include "ui/base/resource/resource_bundle.h"
19 34
20 namespace { 35 namespace {
21 36
22 // Converts a host name into a domain name for easier matching. 37 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
23 std::string GetDomainFromHost(const std::string& host) { 38
24 return net::registry_controlled_domains::GetDomainAndRegistry( 39 // These preferences must be kept in sync with the TrackedPreference enum in
25 host, 40 // tools/metrics/histograms/histograms.xml. To add a new preference, append it
26 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 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 prefs::kExtensionsPref,
49 prefs::kGoogleServicesLastUsername,
50 prefs::kSearchProviderOverrides,
51 prefs::kDefaultSearchProviderSearchURL,
52 prefs::kDefaultSearchProviderKeyword,
53 prefs::kDefaultSearchProviderName,
54 prefs::kPinnedTabs,
55 };
56
57 bool BrowserShuttingDown() {
58 return browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID;
27 } 59 }
28 60
29 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
30
31 } // namespace 61 } // namespace
32 62
33 PrefMetricsService::PrefMetricsService(Profile* profile) 63 PrefMetricsService::PrefMetricsService(Profile* profile)
34 : profile_(profile) { 64 : weak_factory_(this),
65 profile_(profile),
66 prefs_(profile_->GetPrefs()),
67 local_state_(g_browser_process->local_state()),
68 tracked_pref_paths_(kTrackedPrefs),
69 tracked_pref_path_count_(arraysize(kTrackedPrefs)) {
70 // Check that the pref is registered; otherwise some browser_tests crash.
71 if (prefs_->FindPreference(prefs::kGoogleServicesUsername))
72 profile_name_ = profile_->GetProfileName();
73
74 pref_hash_seed_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
75 IDR_PREF_HASH_SEED_BIN).as_string();
76
35 RecordLaunchPrefs(); 77 RecordLaunchPrefs();
36 78
79 // Don't track preferences in browser tests. Otherwise, tests that change
80 // prefs after shutdown begins will write to local state, which will DCHECK
81 // when trying to write.
82 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType))
83 InitializePrefObservers();
84
85 #if !defined(OS_ANDROID)
86 // We need the machine id to compute pref value hashes. Fetch that, and then
87 // call CheckTrackedPreferences in the callback.
88 extensions::api::DeviceId::GetDeviceId(
89 "PrefMetricsService", // non-empty string to obfuscate the device id.
90 Bind(&PrefMetricsService::GetDeviceIdCallback,
91 weak_factory_.GetWeakPtr()));
92 #else
93 // Android has no GetDeviceId.
94 CheckTrackedPreferences();
95 #endif // !defined(OS_ANDROID)
96
37 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 97 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
38 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); 98 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs));
39 99
40 RegisterSyncedPrefObservers(); 100 RegisterSyncedPrefObservers();
41 } 101 }
42 102
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 device_id_(device_id),
113 tracked_pref_paths_(tracked_pref_paths),
114 tracked_pref_path_count_(tracked_pref_path_count) {
115 CheckTrackedPreferences();
116 InitializePrefObservers();
117 }
118
43 PrefMetricsService::~PrefMetricsService() { 119 PrefMetricsService::~PrefMetricsService() {
44 } 120 }
45 121
46 void PrefMetricsService::RecordLaunchPrefs() { 122 void PrefMetricsService::RecordLaunchPrefs() {
47 PrefService* prefs = profile_->GetPrefs(); 123 bool show_home_button = prefs_->GetBoolean(prefs::kShowHomeButton);
48 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); 124 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); 125 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button);
51 if (show_home_button) { 126 if (show_home_button) {
52 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", 127 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage",
53 home_page_is_ntp); 128 home_page_is_ntp);
54 } 129 }
55 130
56 // For non-NTP homepages, see if the URL comes from the same TLD+1 as a known 131 // 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 132 // 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 133 // 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 134 // 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 135 // (pages that share a TLD+1 with a known engine but aren't actually search
61 // pages, e.g. plus.google.com). 136 // pages, e.g. plus.google.com).
62 if (!home_page_is_ntp) { 137 if (!home_page_is_ntp) {
63 GURL homepage_url(prefs->GetString(prefs::kHomePage)); 138 GURL homepage_url(prefs_->GetString(prefs::kHomePage));
64 if (homepage_url.is_valid()) { 139 if (homepage_url.is_valid()) {
65 UMA_HISTOGRAM_ENUMERATION( 140 UMA_HISTOGRAM_ENUMERATION(
66 "Settings.HomePageEngineType", 141 "Settings.HomePageEngineType",
67 TemplateURLPrepopulateData::GetEngineType(homepage_url), 142 TemplateURLPrepopulateData::GetEngineType(homepage_url),
68 SEARCH_ENGINE_MAX); 143 SEARCH_ENGINE_MAX);
69 } 144 }
70 } 145 }
71 146
72 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); 147 int restore_on_startup = prefs_->GetInteger(prefs::kRestoreOnStartup);
73 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", 148 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings",
74 restore_on_startup, kSessionStartupPrefValueMax); 149 restore_on_startup, kSessionStartupPrefValueMax);
75 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { 150 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) {
76 const ListValue* url_list = prefs->GetList(prefs::kURLsToRestoreOnStartup); 151 const ListValue* url_list = prefs_->GetList(prefs::kURLsToRestoreOnStartup);
77 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.StartupPageLoadURLs", 152 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.StartupPageLoadURLs",
78 url_list->GetSize(), 1, 50, 20); 153 url_list->GetSize(), 1, 50, 20);
79 // Similarly, check startup pages for known search engine TLD+1s. 154 // Similarly, check startup pages for known search engine TLD+1s.
80 std::string url_text; 155 std::string url_text;
81 for (size_t i = 0; i < url_list->GetSize(); i++) { 156 for (size_t i = 0; i < url_list->GetSize(); i++) {
82 if (url_list->GetString(i, &url_text)) { 157 if (url_list->GetString(i, &url_text)) {
83 GURL start_url(url_text); 158 GURL start_url(url_text);
84 if (start_url.is_valid()) { 159 if (start_url.is_valid()) {
85 UMA_HISTOGRAM_ENUMERATION( 160 UMA_HISTOGRAM_ENUMERATION(
86 "Settings.StartupPageEngineTypes", 161 "Settings.StartupPageEngineTypes",
87 TemplateURLPrepopulateData::GetEngineType(start_url), 162 TemplateURLPrepopulateData::GetEngineType(start_url),
88 SEARCH_ENGINE_MAX); 163 SEARCH_ENGINE_MAX);
89 } 164 }
90 } 165 }
91 } 166 }
92 } 167 }
93 } 168 }
94 169
170 // static
171 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
172 // Register the top level dictionary to map profile names to dictionaries of
173 // tracked preferences.
174 registry->RegisterDictionaryPref(prefs::kProfilePreferenceHashes);
175 }
176
95 void PrefMetricsService::RegisterSyncedPrefObservers() { 177 void PrefMetricsService::RegisterSyncedPrefObservers() {
96 LogHistogramValueCallback booleanHandler = base::Bind( 178 LogHistogramValueCallback booleanHandler = base::Bind(
97 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); 179 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this));
98 180
99 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); 181 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler);
100 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", 182 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage",
101 booleanHandler); 183 booleanHandler);
102 184
103 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", 185 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings",
104 base::Bind(&PrefMetricsService::LogIntegerPrefChange, 186 base::Bind(&PrefMetricsService::LogIntegerPrefChange,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 const ListValue* items = NULL; 244 const ListValue* items = NULL;
163 if (!value->GetAsList(&items)) 245 if (!value->GetAsList(&items))
164 return; 246 return;
165 for (size_t i = 0; i < items->GetSize(); ++i) { 247 for (size_t i = 0; i < items->GetSize(); ++i) {
166 const Value *item_value = NULL; 248 const Value *item_value = NULL;
167 if (items->Get(i, &item_value)) 249 if (items->Get(i, &item_value))
168 item_callback.Run(histogram_name, item_value); 250 item_callback.Run(histogram_name, item_value);
169 } 251 }
170 } 252 }
171 253
254 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) {
255 device_id_ = device_id;
256 CheckTrackedPreferences();
257 }
258
259 // To detect changes to Preferences that happen outside of Chrome, we hash
260 // selected pref values and save them in local state. CheckTrackedPreferences
261 // compares the saved values to the values observed in the profile's prefs. A
262 // dictionary of dictionaries in local state holds the hashed values, grouped by
263 // profile. To make the system more resistant to spoofing, pref values are
264 // hashed with the pref path and the device id.
265 void PrefMetricsService::CheckTrackedPreferences() {
266 // Don't attempt to change local state during browser shutdown.
267 if (BrowserShuttingDown())
battre 2013/09/02 08:33:46 Don't you want to move this down so that it become
bbudge 2013/09/04 15:00:09 I added these guards to get some browser_tests to
268 return;
269
270 const base::DictionaryValue* pref_hash_dicts =
271 local_state_->GetDictionary(prefs::kProfilePreferenceHashes);
272 // Get the hashed prefs dictionary if it exists. If it doesn't, it will be
273 // created if we set preference values below.
274 const base::DictionaryValue* hashed_prefs = NULL;
275 pref_hash_dicts->GetDictionary(profile_name_, &hashed_prefs);
276 for (int i = 0; i < tracked_pref_path_count_; ++i) {
277 // Skip prefs that haven't been registered (e.g. in browser_tests).
278 if (!prefs_->FindPreference(tracked_pref_paths_[i]))
279 continue;
280
281 bool unchanged = true;
battre 2013/09/02 08:33:46 how about removing one negation and calling it cha
bbudge 2013/09/04 15:00:09 Done.
282 const base::Value* value = prefs_->GetUserPrefValue(tracked_pref_paths_[i]);
283 if (value) {
284 std::string value_hash =
285 GetHashedPrefValue(tracked_pref_paths_[i], value);
286 std::string last_hash;
287 if (hashed_prefs &&
288 hashed_prefs->GetString(tracked_pref_paths_[i], &last_hash)) {
289 if (value_hash != last_hash) {
290 unchanged = false;
291 // Record that the preference changed from its last value.
292 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged",
293 i, tracked_pref_path_count_);
294 UpdateTrackedPreference(tracked_pref_paths_[i]);
295 }
296 } else {
297 unchanged = false;
298 // Record that we haven't tracked this preference yet, or the hash in
299 // local state was removed.
300 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized",
301 i, tracked_pref_path_count_);
302 UpdateTrackedPreference(tracked_pref_paths_[i]);
303 }
304 } else {
305 // There is no preference set. Remove any hashed value from local state
306 // and if one was present, record that a pref was cleared.
307 if (RemoveTrackedPreference(tracked_pref_paths_[i])) {
308 unchanged = false;
309 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceCleared",
310 i, tracked_pref_path_count_);
311 }
312 }
313 if (unchanged) {
314 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceUnchanged",
315 i, tracked_pref_path_count_);
316 }
317 }
318 }
319
320 void PrefMetricsService::UpdateTrackedPreference(const char* path) {
321 // Don't attempt to change local state during browser shutdown.
322 if (BrowserShuttingDown())
323 return;
324
325 const base::Value* value = prefs_->GetUserPrefValue(path);
326 if (value) {
327 DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
328 base::DictionaryValue* dict = update.Get();
329 if (dict) {
330 dict->SetString(GetHashedPrefPath(path),
331 GetHashedPrefValue(path, value));
332 }
333 } else {
334 RemoveTrackedPreference(path);
335 }
336 }
337
338 bool PrefMetricsService::RemoveTrackedPreference(const char* path) {
339 DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
340 base::DictionaryValue* dict = update.Get();
341 if (dict)
342 return dict->Remove(GetHashedPrefPath(path), NULL);
343
344 return false;
345 }
346
347 std::string PrefMetricsService::GetHashedPrefPath(const char* path) {
348 std::string hash_pref_path(profile_name_);
349 hash_pref_path.append(".");
350 hash_pref_path.append(path);
351 return hash_pref_path;
352 }
353
354 std::string PrefMetricsService::GetHashedPrefValue(
355 const char* path,
356 const base::Value* value) {
357 DCHECK(value);
358 std::string value_string;
359 JSONStringValueSerializer serializer(&value_string);
360 serializer.Serialize(*value);
battre 2013/09/02 08:33:46 This is pretty brittle. If somebody modifies the s
bbudge 2013/09/04 15:00:09 Good point. I added some test code to protect agai
361
362 base::MD5Context context;
363 base::MD5Init(&context);
364 // Salt the hash with an initial seed, the device id, and the pref path.
365 base::MD5Update(&context, pref_hash_seed_);
366 base::MD5Update(&context, device_id_);
367 base::MD5Update(&context, path);
368 base::MD5Update(&context, value_string);
369 base::MD5Digest digest;
370 base::MD5Final(&digest, &context);
371 return base::MD5DigestToBase16(digest);
372 }
373
374 void PrefMetricsService::InitializePrefObservers() {
375 pref_registrar_.Init(prefs_);
376 for (int i = 0; i < tracked_pref_path_count_; ++i) {
377 pref_registrar_.Add(
378 tracked_pref_paths_[i],
379 base::Bind(&PrefMetricsService::UpdateTrackedPreference,
380 weak_factory_.GetWeakPtr(),
381 tracked_pref_paths_[i]));
382 }
383 }
384
172 // static 385 // static
173 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { 386 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() {
174 return Singleton<PrefMetricsService::Factory>::get(); 387 return Singleton<PrefMetricsService::Factory>::get();
175 } 388 }
176 389
177 // static 390 // static
178 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( 391 PrefMetricsService* PrefMetricsService::Factory::GetForProfile(
179 Profile* profile) { 392 Profile* profile) {
180 return static_cast<PrefMetricsService*>( 393 return static_cast<PrefMetricsService*>(
181 GetInstance()->GetServiceForBrowserContext(profile, true)); 394 GetInstance()->GetServiceForBrowserContext(profile, true));
(...skipping 19 matching lines...) Expand all
201 } 414 }
202 415
203 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { 416 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const {
204 return false; 417 return false;
205 } 418 }
206 419
207 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( 420 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse(
208 content::BrowserContext* context) const { 421 content::BrowserContext* context) const {
209 return chrome::GetBrowserContextRedirectedInIncognito(context); 422 return chrome::GetBrowserContextRedirectedInIncognito(context);
210 } 423 }
OLDNEW
« no previous file with comments | « chrome/browser/prefs/pref_metrics_service.h ('k') | chrome/browser/prefs/pref_metrics_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698