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

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: Add TODO for Device ID API use. 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 // Accessing the Device ID API here is a layering violation.
16 // TODO(bbudge) Move the API so it's usable here. See crbug.com/276485.
17 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
18 #include "chrome/browser/extensions/extension_prefs.h"
10 #include "chrome/browser/prefs/pref_service_syncable.h" 19 #include "chrome/browser/prefs/pref_service_syncable.h"
20 #include "chrome/browser/prefs/scoped_user_pref_update.h"
11 #include "chrome/browser/prefs/session_startup_pref.h" 21 #include "chrome/browser/prefs/session_startup_pref.h"
12 #include "chrome/browser/prefs/synced_pref_change_registrar.h" 22 #include "chrome/browser/prefs/synced_pref_change_registrar.h"
13 #include "chrome/browser/profiles/incognito_helpers.h" 23 #include "chrome/browser/profiles/incognito_helpers.h"
14 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/pref_names.h" 25 #include "chrome/common/pref_names.h"
16 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" 26 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h"
17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 27 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
18 28
19 namespace { 29 namespace {
20 30
21 // Converts a host name into a domain name for easier matching. 31 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
22 std::string GetDomainFromHost(const std::string& host) { 32
23 return net::registry_controlled_domains::GetDomainAndRegistry( 33 // These preferences must be kept in sync with the TrackedPreference enum in
24 host, 34 // tools/metrics/histograms/histograms.xml. To add a new preference, append it
25 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 35 // to the array and add a corresponding value to the histogram enum.
36 const char* kTrackedPrefs[] = {
37 prefs::kShowHomeButton,
38 prefs::kHomePageIsNewTabPage,
39 prefs::kHomePage,
40 prefs::kRestoreOnStartup,
41 prefs::kURLsToRestoreOnStartup,
42 extensions::ExtensionPrefs::kExtensionsPref,
43 };
44
45 bool BrowserShuttingDown() {
46 return browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID;
26 } 47 }
27 48
28 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
29
30 } // namespace 49 } // namespace
31 50
32 PrefMetricsService::PrefMetricsService(Profile* profile) 51 PrefMetricsService::PrefMetricsService(Profile* profile)
33 : profile_(profile) { 52 : weak_factory_(this),
53 profile_(profile),
54 prefs_(profile_->GetPrefs()),
55 local_state_(g_browser_process->local_state()),
56 tracked_pref_paths_(kTrackedPrefs),
57 tracked_pref_path_count_(arraysize(kTrackedPrefs)) {
58 // Get the profile name (some browser tests don't set this up, in which
59 // case profile_name_ is the empty string).
60 if (prefs_->FindPreference(prefs::kGoogleServicesUsername))
61 profile_name_ = profile_->GetProfileName();
62
34 RecordLaunchPrefs(); 63 RecordLaunchPrefs();
35 64
65 #if !defined(OS_ANDROID)
66 // We need the machine id to compute pref value hashes. Fetch that, and then
67 // call CheckTrackedPreferences in the callback.
68 extensions::api::DeviceId::GetDeviceId(
69 "PrefMetricsService", // non-empty string to obfuscate the device id.
70 Bind(&PrefMetricsService::GetDeviceIdCallback,
71 weak_factory_.GetWeakPtr()));
72 #else
73 // Android has no GetDeviceId.
74 CheckTrackedPreferences();
75 #endif // !defined(OS_ANDROID)
76
77 InitializePrefObservers();
78
36 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 79 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
37 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); 80 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs));
38 81
39 RegisterSyncedPrefObservers(); 82 RegisterSyncedPrefObservers();
40 } 83 }
41 84
85 PrefMetricsService::PrefMetricsService(Profile* profile,
86 PrefService* local_state,
87 const std::string& device_id,
88 const char** tracked_pref_paths,
89 int tracked_pref_path_count)
90 : weak_factory_(this),
91 profile_(profile),
92 prefs_(profile->GetPrefs()),
93 local_state_(local_state),
94 device_id_(device_id),
95 tracked_pref_paths_(tracked_pref_paths),
96 tracked_pref_path_count_(tracked_pref_path_count) {
97 CheckTrackedPreferences();
98 InitializePrefObservers();
99 }
100
42 PrefMetricsService::~PrefMetricsService() { 101 PrefMetricsService::~PrefMetricsService() {
43 } 102 }
44 103
45 void PrefMetricsService::RecordLaunchPrefs() { 104 void PrefMetricsService::RecordLaunchPrefs() {
46 PrefService* prefs = profile_->GetPrefs(); 105 bool show_home_button = prefs_->GetBoolean(prefs::kShowHomeButton);
47 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); 106 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); 107 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button);
50 if (show_home_button) { 108 if (show_home_button) {
51 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", 109 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage",
52 home_page_is_ntp); 110 home_page_is_ntp);
53 } 111 }
54 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); 112 int restore_on_startup = prefs_->GetInteger(prefs::kRestoreOnStartup);
55 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", 113 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings",
56 restore_on_startup, kSessionStartupPrefValueMax); 114 restore_on_startup, kSessionStartupPrefValueMax);
57 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { 115 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) {
58 const int url_list_size = prefs->GetList( 116 const int url_list_size = prefs_->GetList(
59 prefs::kURLsToRestoreOnStartup)->GetSize(); 117 prefs::kURLsToRestoreOnStartup)->GetSize();
60 UMA_HISTOGRAM_CUSTOM_COUNTS( 118 UMA_HISTOGRAM_CUSTOM_COUNTS(
61 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); 119 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20);
62 } 120 }
63 } 121 }
64 122
123 // static
124 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
125 // Register the top level dictionary to map profile names to dictionaries of
126 // tracked preferences.
127 registry->RegisterDictionaryPref(prefs::kProfilePreferenceHashes);
128 }
129
65 void PrefMetricsService::RegisterSyncedPrefObservers() { 130 void PrefMetricsService::RegisterSyncedPrefObservers() {
66 LogHistogramValueCallback booleanHandler = base::Bind( 131 LogHistogramValueCallback booleanHandler = base::Bind(
67 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); 132 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this));
68 133
69 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); 134 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler);
70 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", 135 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage",
71 booleanHandler); 136 booleanHandler);
72 137
73 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", 138 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings",
74 base::Bind(&PrefMetricsService::LogIntegerPrefChange, 139 base::Bind(&PrefMetricsService::LogIntegerPrefChange,
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 const ListValue* items = NULL; 197 const ListValue* items = NULL;
133 if (!value->GetAsList(&items)) 198 if (!value->GetAsList(&items))
134 return; 199 return;
135 for (size_t i = 0; i < items->GetSize(); ++i) { 200 for (size_t i = 0; i < items->GetSize(); ++i) {
136 const Value *item_value = NULL; 201 const Value *item_value = NULL;
137 if (items->Get(i, &item_value)) 202 if (items->Get(i, &item_value))
138 item_callback.Run(histogram_name, item_value); 203 item_callback.Run(histogram_name, item_value);
139 } 204 }
140 } 205 }
141 206
207 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) {
208 device_id_ = device_id;
209 CheckTrackedPreferences();
210 }
211
212 // In order to detect changes to Preferences that happen outside of Chrome, we
213 // save selected pref values in local state. CheckTrackedPreferences compares
214 // the saved values to the values observed in the profile's prefs. A dictionary
215 // of dictionaries in local state holds the saved values, grouped by profile.
216 // To make the system more resistant to spoofing, pref values are hashed with
217 // the pref path and the device id.
218 void PrefMetricsService::CheckTrackedPreferences() {
219 UMA_HISTOGRAM_BOOLEAN("Settings.TrackedPreferencesChecked", true);
220
221 const base::DictionaryValue* pref_hash_dicts =
222 local_state_->GetDictionary(prefs::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_, prefs::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_, prefs::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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698