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

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: Make sure local_state is initialized. Created 7 years, 4 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/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"
Mattias Nissler (ping if slow) 2013/08/15 10:14:51 I'd consider this a layering violation. chrome/bro
bbudge 2013/08/15 17:33:49 This appears to be the only way to get a unique pe
Mattias Nissler (ping if slow) 2013/08/20 12:53:54 That'd be appreciated. Can you file a bug and put
bbudge 2013/08/20 18:17:50 Done.
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 Settings. When Chrome is started up, we compare
Mattias Nissler (ping if slow) 2013/08/15 10:14:51 local state, here and below
bbudge 2013/08/15 17:33:49 Done.
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 Settings to hold the saved
35 // values, grouped by profile. To make the system faster and more resistant to
Mattias Nissler (ping if slow) 2013/08/15 10:14:51 I don't think hashing makes things faster, given t
bbudge 2013/08/15 17:33:49 I was thinking of I/O and comparing values, but I
36 // spoofing, each 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,
Mattias Nissler (ping if slow) 2013/08/15 10:14:51 FWIW, this should be defined in pref_names.{h,cc}
bbudge 2013/08/15 17:33:49 Perhaps a separate CL.
49 };
50
51 const int kTrackedPrefsCount = arraysize(kTrackedPrefs);
52
53 bool BrowserShuttingDown() {
54 return browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID;
26 } 55 }
27 56
28 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
29
30 } // namespace 57 } // namespace
31 58
32 PrefMetricsService::PrefMetricsService(Profile* profile) 59 PrefMetricsService::PrefMetricsService(Profile* profile)
33 : profile_(profile) { 60 : weak_factory_(this),
61 profile_(profile) {
34 RecordLaunchPrefs(); 62 RecordLaunchPrefs();
35 63
64 // Make sure local state is initialized.
65 g_browser_process->local_state();
Mattias Nissler (ping if slow) 2013/08/15 10:14:51 This shouldn't be required, local state gets initi
bbudge 2013/08/15 17:33:49 I thought it might fix some tests that are crashin
66
67 #if !defined(OS_ANDROID)
68 // We need the machine id to compute pref value hashes. Fetch that, and then
69 // call CheckTrackedPreferences in the callback.
70 extensions::api::DeviceId::GetDeviceId(
71 "PrefMetricsService", // non-empty string to obfuscate the device id.
72 Bind(&PrefMetricsService::GetDeviceIdCallback,
73 weak_factory_.GetWeakPtr()));
74 #else
75 // Android has no GetDeviceId.
76 CheckTrackedPreferences();
77 #endif // !defined(OS_ANDROID)
78
79 // It's tricky to save preference value hashes on browser shutdown. Instead,
80 // register for notifications when tracked preferences change so we can update
81 // the hashes in Local Settings.
82 pref_registrar_.Init(profile_->GetPrefs());
83 for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) {
84 pref_registrar_.Add(
85 kTrackedPrefs[i],
86 base::Bind(&PrefMetricsService::UpdateTrackedPreference,
87 weak_factory_.GetWeakPtr(),
88 kTrackedPrefs[i]));
89 }
90
36 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 91 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
37 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); 92 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs));
38 93
39 RegisterSyncedPrefObservers(); 94 RegisterSyncedPrefObservers();
40 } 95 }
41 96
42 PrefMetricsService::~PrefMetricsService() { 97 PrefMetricsService::~PrefMetricsService() {
43 } 98 }
44 99
45 void PrefMetricsService::RecordLaunchPrefs() { 100 void PrefMetricsService::RecordLaunchPrefs() {
46 PrefService* prefs = profile_->GetPrefs(); 101 PrefService* prefs = profile_->GetPrefs();
47 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); 102 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton);
48 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); 103 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage);
49 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); 104 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button);
50 if (show_home_button) { 105 if (show_home_button) {
51 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", 106 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage",
52 home_page_is_ntp); 107 home_page_is_ntp);
53 } 108 }
54 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); 109 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup);
55 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", 110 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings",
56 restore_on_startup, kSessionStartupPrefValueMax); 111 restore_on_startup, kSessionStartupPrefValueMax);
57 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { 112 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) {
58 const int url_list_size = prefs->GetList( 113 const int url_list_size = prefs->GetList(
59 prefs::kURLsToRestoreOnStartup)->GetSize(); 114 prefs::kURLsToRestoreOnStartup)->GetSize();
60 UMA_HISTOGRAM_CUSTOM_COUNTS( 115 UMA_HISTOGRAM_CUSTOM_COUNTS(
61 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); 116 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20);
62 } 117 }
63 } 118 }
64 119
120 // static
121 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
122 // Register the top level dictionary to map profile names to dictionaries of
123 // tracked preferences.
124 registry->RegisterDictionaryPref(kProfilePreferenceHashes);
125 }
126
65 void PrefMetricsService::RegisterSyncedPrefObservers() { 127 void PrefMetricsService::RegisterSyncedPrefObservers() {
66 LogHistogramValueCallback booleanHandler = base::Bind( 128 LogHistogramValueCallback booleanHandler = base::Bind(
67 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); 129 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this));
68 130
69 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); 131 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler);
70 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", 132 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage",
71 booleanHandler); 133 booleanHandler);
72 134
73 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", 135 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings",
74 base::Bind(&PrefMetricsService::LogIntegerPrefChange, 136 base::Bind(&PrefMetricsService::LogIntegerPrefChange,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 const ListValue* items = NULL; 194 const ListValue* items = NULL;
133 if (!value->GetAsList(&items)) 195 if (!value->GetAsList(&items))
134 return; 196 return;
135 for (size_t i = 0; i < items->GetSize(); ++i) { 197 for (size_t i = 0; i < items->GetSize(); ++i) {
136 const Value *item_value = NULL; 198 const Value *item_value = NULL;
137 if (items->Get(i, &item_value)) 199 if (items->Get(i, &item_value))
138 item_callback.Run(histogram_name, item_value); 200 item_callback.Run(histogram_name, item_value);
139 } 201 }
140 } 202 }
141 203
204 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) {
205 device_id_ = device_id;
206 CheckTrackedPreferences();
207 }
208
209 void PrefMetricsService::CheckTrackedPreferences() {
210 PrefService* local_state = g_browser_process->local_state();
211 const base::DictionaryValue* profiles =
212 local_state->GetDictionary(kProfilePreferenceHashes);
213 DCHECK(profiles);
214 // Get the hashed prefs dictionary if it exists. If it doesn't, it will be
215 // created if we set preference values below.
216 const base::DictionaryValue* hashed_prefs = NULL;
217 profiles->GetDictionary(profile_->GetProfileName(), &hashed_prefs);
218 const PrefService* pref_service = profile_->GetPrefs();
219 for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) {
220 const base::Value* value =
221 pref_service->GetUserPrefValue(kTrackedPrefs[i]);
222 if (value) {
223 std::string value_hash = GetHashedPrefValue(kTrackedPrefs[i], value);
224 std::string last_hash;
225 if (hashed_prefs &&
226 hashed_prefs->GetString(kTrackedPrefs[i], &last_hash)) {
227 if (value_hash != last_hash) {
228 // Record that the preference changed from its last value.
229 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged",
230 i, arraysize(kTrackedPrefs));
231 UpdateTrackedPreference(kTrackedPrefs[i]);
232 }
233 } else {
234 // Record that we haven't tracked this preference yet, or the hash in
235 // local settings was removed.
236 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized",
237 i, arraysize(kTrackedPrefs));
238 UpdateTrackedPreference(kTrackedPrefs[i]);
239 }
240 } else {
241 // There is no preference set. Remove any hashed value from local settings
242 // and if one was present, record that a pref was removed.
243 if (RemoveTrackedPreference(kTrackedPrefs[i])) {
244 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceRemoved",
245 i, arraysize(kTrackedPrefs));
246 }
247 }
248 }
249 }
250
251 void PrefMetricsService::UpdateTrackedPreference(const char* path) {
252 // Don't try to change local settings during browser shutdown.
253 if (BrowserShuttingDown())
254 return;
255
256 PrefService* pref_service = profile_->GetPrefs();
257 const base::Value* value = pref_service->GetUserPrefValue(path);
258 if (value) {
259 PrefService* local_state = g_browser_process->local_state();
260 DictionaryPrefUpdate update(local_state, kProfilePreferenceHashes);
261 base::DictionaryValue* prefs = update.Get();
262 if (prefs) {
263 prefs->SetString(GetHashedPrefPath(path),
264 GetHashedPrefValue(path, value));
265 }
266 } else {
267 RemoveTrackedPreference(path);
268 }
269 }
270
271 bool PrefMetricsService::RemoveTrackedPreference(const char* path) {
272 // Don't try to change local settings during browser shutdown.
273 if (BrowserShuttingDown())
274 return false;
275
276 PrefService* local_state = g_browser_process->local_state();
277 DictionaryPrefUpdate update(local_state, kProfilePreferenceHashes);
278 base::DictionaryValue* prefs = update.Get();
279 if (prefs)
280 return prefs->Remove(GetHashedPrefPath(path), NULL);
281
282 return false;
283 }
284
285 std::string PrefMetricsService::GetHashedPrefPath(const char* path) {
286 std::string hash_pref_path(profile_->GetProfileName());
287 hash_pref_path.append(".");
288 hash_pref_path.append(path);
289 return hash_pref_path;
290 }
291
292 std::string PrefMetricsService::GetHashedPrefValue(
293 const char* path,
294 const base::Value* value) {
295 DCHECK(value);
296 std::string value_string;
297 JSONStringValueSerializer serializer(&value_string);
298 serializer.Serialize(*value);
299
300 base::MD5Context context;
301 base::MD5Init(&context);
302 base::MD5Update(&context, device_id_); // Salt with device id.
303 base::MD5Update(&context, path); // Salt with pref path.
304 base::MD5Update(&context, value_string);
305 base::MD5Digest digest;
306 base::MD5Final(&digest, &context);
307 return base::MD5DigestToBase16(digest);
308 }
309
142 // static 310 // static
143 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { 311 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() {
144 return Singleton<PrefMetricsService::Factory>::get(); 312 return Singleton<PrefMetricsService::Factory>::get();
145 } 313 }
146 314
147 // static 315 // static
148 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( 316 PrefMetricsService* PrefMetricsService::Factory::GetForProfile(
149 Profile* profile) { 317 Profile* profile) {
150 return static_cast<PrefMetricsService*>( 318 return static_cast<PrefMetricsService*>(
151 GetInstance()->GetServiceForBrowserContext(profile, true)); 319 GetInstance()->GetServiceForBrowserContext(profile, true));
(...skipping 19 matching lines...) Expand all
171 } 339 }
172 340
173 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { 341 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const {
174 return false; 342 return false;
175 } 343 }
176 344
177 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( 345 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse(
178 content::BrowserContext* context) const { 346 content::BrowserContext* context) const {
179 return chrome::GetBrowserContextRedirectedInIncognito(context); 347 return chrome::GetBrowserContextRedirectedInIncognito(context);
180 } 348 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698