Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/metrics/variations_service.h" | 5 #include "chrome/browser/metrics/variations_service.h" |
| 6 | 6 |
| 7 #include "base/base64.h" | 7 #include "base/base64.h" |
| 8 #include "base/build_time.h" | 8 #include "base/build_time.h" |
| 9 #include "base/version.h" | 9 #include "base/version.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/memory/singleton.h" | 11 #include "base/memory/singleton.h" |
| 12 #include "base/metrics/field_trial.h" | |
| 12 #include "chrome/browser/browser_process.h" | 13 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/metrics/proto/trials_seed.pb.h" | 14 #include "chrome/browser/metrics/proto/trials_seed.pb.h" |
| 14 #include "chrome/browser/prefs/pref_service.h" | 15 #include "chrome/browser/prefs/pref_service.h" |
| 15 #include "chrome/common/pref_names.h" | 16 #include "chrome/common/pref_names.h" |
| 16 #include "content/public/common/url_fetcher.h" | 17 #include "content/public/common/url_fetcher.h" |
| 17 #include "googleurl/src/gurl.h" | 18 #include "googleurl/src/gurl.h" |
| 18 #include "net/base/load_flags.h" | 19 #include "net/base/load_flags.h" |
| 19 #include "net/url_request/url_request_status.h" | 20 #include "net/url_request/url_request_status.h" |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 43 return chrome::VersionInfo::CHANNEL_UNKNOWN; | 44 return chrome::VersionInfo::CHANNEL_UNKNOWN; |
| 44 } | 45 } |
| 45 | 46 |
| 46 } // namespace | 47 } // namespace |
| 47 | 48 |
| 48 // Static | 49 // Static |
| 49 VariationsService* VariationsService::GetInstance() { | 50 VariationsService* VariationsService::GetInstance() { |
| 50 return Singleton<VariationsService>::get(); | 51 return Singleton<VariationsService>::get(); |
| 51 } | 52 } |
| 52 | 53 |
| 53 void VariationsService::LoadVariationsSeed(PrefService* local_prefs) { | 54 bool VariationsService::CreateTrialsFromSeed(PrefService* local_prefs) { |
| 54 std::string base64_seed_data = local_prefs->GetString(prefs::kVariationsSeed); | 55 LOG(INFO) << "Creating trials from seed"; |
|
MAD
2012/05/08 03:09:00
Either remove all LOGs or use DVLOG(1)s
jwd
2012/05/08 03:30:01
Done.
| |
| 55 std::string seed_data; | 56 chrome_variations::TrialsSeed seed; |
| 57 if (!LoadVariationsSeed(local_prefs, seed)) | |
| 58 return false; | |
| 56 | 59 |
| 57 // If the decode process fails, assume the pref value is corrupt, and clear | 60 LOG(INFO) << "about to loop through studies " << seed.study_size(); |
| 58 // it. | 61 for (int i = 0; i<seed.study_size(); ++i) { |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put spaces around the <.
jwd
2012/05/08 03:35:18
Done.
| |
| 59 if (!base::Base64Decode(base64_seed_data, &seed_data) || | 62 CreateTrialFromStudy(seed.study(i)); |
|
MAD
2012/05/08 03:09:00
For single line block, we don't use {}
jwd
2012/05/08 03:30:01
Done.
| |
| 60 !variations_seed_.ParseFromString(seed_data)) { | |
| 61 VLOG(1) << "Variations Seed data in local pref is corrupt, clearing the " | |
| 62 << "pref."; | |
| 63 local_prefs->ClearPref(prefs::kVariationsSeed); | |
| 64 } | 63 } |
| 64 return true; | |
| 65 } | 65 } |
| 66 | 66 |
| 67 void VariationsService::StartFetchingVariationsSeed() { | 67 void VariationsService::StartFetchingVariationsSeed() { |
| 68 LOG(INFO) << "start fetching " << kDefaultVariationsServer; | |
| 68 pending_seed_request_.reset(content::URLFetcher::Create( | 69 pending_seed_request_.reset(content::URLFetcher::Create( |
| 69 GURL(kDefaultVariationsServer), content::URLFetcher::GET, this)); | 70 GURL(kDefaultVariationsServer), content::URLFetcher::GET, this)); |
| 70 pending_seed_request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | 71 pending_seed_request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
| 71 net::LOAD_DO_NOT_SAVE_COOKIES); | 72 net::LOAD_DO_NOT_SAVE_COOKIES); |
| 72 pending_seed_request_->SetRequestContext( | 73 pending_seed_request_->SetRequestContext( |
| 73 g_browser_process->system_request_context()); | 74 g_browser_process->system_request_context()); |
| 75 // TODO(jwd): pull max retries into a contsant | |
| 74 pending_seed_request_->SetMaxRetries(5); | 76 pending_seed_request_->SetMaxRetries(5); |
| 75 pending_seed_request_->Start(); | 77 pending_seed_request_->Start(); |
| 76 } | 78 } |
| 77 | 79 |
| 78 void VariationsService::OnURLFetchComplete(const content::URLFetcher* source) { | 80 void VariationsService::OnURLFetchComplete(const content::URLFetcher* source) { |
| 81 LOG(INFO) << "got server response"; | |
| 79 DCHECK_EQ(pending_seed_request_.get(), source); | 82 DCHECK_EQ(pending_seed_request_.get(), source); |
| 80 // When we're done handling the request, the fetcher will be deleted. | 83 // When we're done handling the request, the fetcher will be deleted. |
| 81 scoped_ptr<const content::URLFetcher> request( | 84 scoped_ptr<const content::URLFetcher> request( |
| 82 pending_seed_request_.release()); | 85 pending_seed_request_.release()); |
| 83 if (request->GetStatus().status() != net::URLRequestStatus::SUCCESS || | 86 if (request->GetStatus().status() != net::URLRequestStatus::SUCCESS || |
| 84 request->GetResponseCode() != 200) | 87 request->GetResponseCode() != 200) { |
| 88 LOG(INFO) << "not a success"; | |
| 85 return; | 89 return; |
| 86 | 90 } |
| 91 LOG(INFO) << " response was success"; | |
| 87 std::string seed_data; | 92 std::string seed_data; |
| 88 request->GetResponseAsString(&seed_data); | 93 request->GetResponseAsString(&seed_data); |
| 89 | 94 |
| 90 StoreSeedData(seed_data, g_browser_process->local_state()); | 95 StoreSeedData(seed_data, g_browser_process->local_state()); |
| 91 } | 96 } |
| 92 | 97 |
| 93 // static | 98 // static |
| 94 void VariationsService::RegisterPrefs(PrefService* prefs) { | 99 void VariationsService::RegisterPrefs(PrefService* prefs) { |
| 95 prefs->RegisterStringPref(prefs::kVariationsSeed, std::string()); | 100 prefs->RegisterStringPref(prefs::kVariationsSeed, std::string()); |
| 96 } | 101 } |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 139 } | 144 } |
| 140 | 145 |
| 141 DVLOG(1) << "Kept study " << study.name() << "."; | 146 DVLOG(1) << "Kept study " << study.name() << "."; |
| 142 return true; | 147 return true; |
| 143 } | 148 } |
| 144 | 149 |
| 145 // static | 150 // static |
| 146 bool VariationsService::CheckStudyChannel( | 151 bool VariationsService::CheckStudyChannel( |
| 147 const chrome_variations::Study& study, | 152 const chrome_variations::Study& study, |
| 148 chrome::VersionInfo::Channel channel) { | 153 chrome::VersionInfo::Channel channel) { |
| 149 for (int i = 0; i < study.channel_size(); ++i) { | 154 if (study.channel_size()==0) |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put spaces around the ==.
jwd
2012/05/08 03:35:18
Done.
| |
| 155 // An empty channel list matches all channels. | |
|
MAD
2012/05/08 03:09:00
Add {} if you have more than one line, including c
jwd
2012/05/08 03:35:18
Done.
| |
| 156 return true; | |
| 157 | |
| 158 for (int i = 0; i < study.channel_size(); i++) { | |
|
MAD
2012/05/08 03:09:00
i++ -> ++i
jwd
2012/05/08 03:30:01
Done.
| |
| 150 if (ConvertStudyChannelToVersionChannel(study.channel(i)) == channel) | 159 if (ConvertStudyChannelToVersionChannel(study.channel(i)) == channel) |
| 151 return true; | 160 return true; |
| 152 } | 161 } |
| 153 return false; | 162 return false; |
| 154 } | 163 } |
| 155 | 164 |
| 156 // static | 165 // static |
| 157 bool VariationsService::CheckStudyVersion(const chrome_variations::Study& study, | 166 bool VariationsService::CheckStudyVersion(const chrome_variations::Study& study, |
| 158 const std::string& version_string) { | 167 const std::string& version_string) { |
| 159 const Version current_version(version_string); | 168 const Version current_version(version_string); |
| 160 if (!current_version.IsValid()) { | 169 if (!current_version.IsValid()) { |
| 161 DCHECK(false); | 170 DCHECK(false); |
| 162 return false; | 171 return false; |
| 163 } | 172 } |
| 164 | 173 |
| 165 if (study.has_min_version()) { | 174 if (study.has_min_version()) { |
| 166 const Version min_version(study.min_version()); | 175 const Version min_version(study.min_version()); |
| 167 if (!min_version.IsValid()) | 176 if (!min_version.IsValid()){ |
| 177 LOG(INFO) << 1; | |
| 168 return false; | 178 return false; |
| 169 if (current_version.CompareTo(min_version) < 0) | 179 } |
| 180 if (current_version.CompareTo(min_version) < 0){ | |
| 181 LOG(INFO) << 2; | |
| 170 return false; | 182 return false; |
| 183 } | |
| 171 } | 184 } |
| 172 | 185 |
| 173 if (study.has_max_version()) { | 186 if (study.has_max_version()) { |
| 174 const Version max_version(study.max_version()); | 187 const Version max_version(study.max_version()); |
| 175 if (!max_version.IsValid()) | 188 if (!max_version.IsValid()){ |
| 189 LOG(INFO) << 3; | |
| 176 return false; | 190 return false; |
| 177 if (current_version.CompareTo(max_version) > 0) | 191 } |
| 192 if (current_version.CompareTo(max_version) > 0){ | |
| 193 LOG(INFO) << 4; | |
| 178 return false; | 194 return false; |
| 195 } | |
| 179 } | 196 } |
| 180 | 197 |
| 181 return true; | 198 return true; |
| 182 } | 199 } |
| 183 | 200 |
| 184 // static | 201 // static |
| 185 bool VariationsService::CheckStudyDate(const chrome_variations::Study& study, | 202 bool VariationsService::CheckStudyDate(const chrome_variations::Study& study, |
| 186 const base::Time& date_time) { | 203 const base::Time& date_time) { |
| 187 const base::Time epoch = base::Time::UnixEpoch(); | 204 const base::Time epoch = base::Time::UnixEpoch(); |
| 188 | 205 |
| 189 if (study.has_start_date()) { | 206 if (study.has_start_date()) { |
| 190 const base::Time start_date = | 207 const base::Time start_date = |
| 191 epoch + base::TimeDelta::FromMilliseconds(study.start_date()); | 208 epoch + base::TimeDelta::FromSeconds(study.start_date()); |
|
MAD
2012/05/08 03:09:00
Good catch, thanks for fixing...
| |
| 192 if (date_time < start_date) | 209 if (date_time < start_date){ |
| 210 LOG(INFO) << "fail1 "; | |
| 193 return false; | 211 return false; |
| 212 } | |
| 194 } | 213 } |
| 195 | 214 |
| 196 if (study.has_expiry_date()) { | 215 if (study.has_expiry_date()) { |
| 197 const base::Time expiry_date = | 216 const base::Time expiry_date = |
| 198 epoch + base::TimeDelta::FromMilliseconds(study.expiry_date()); | 217 epoch + base::TimeDelta::FromSeconds(study.expiry_date()); |
| 199 if (date_time >= expiry_date) | 218 if (date_time >= expiry_date){ |
| 219 LOG(INFO) << "fail2 "; | |
| 200 return false; | 220 return false; |
| 221 } | |
| 201 } | 222 } |
| 202 | 223 |
| 203 return true; | 224 return true; |
| 204 } | 225 } |
| 226 | |
| 227 bool VariationsService::LoadVariationsSeed( | |
| 228 PrefService* local_prefs, | |
| 229 chrome_variations::TrialsSeed& seed) { | |
| 230 std::string base64_seed_data = local_prefs->GetString(prefs::kVariationsSeed); | |
| 231 std::string seed_data; | |
| 232 | |
| 233 // If the decode process fails, assume the pref value is corrupt, and clear | |
| 234 // it. | |
| 235 if (!base::Base64Decode(base64_seed_data, &seed_data) || | |
| 236 !seed.ParseFromString(seed_data)) { | |
| 237 VLOG(1) << "Variations Seed data in local pref is corrupt, clearing the " | |
| 238 << "pref."; | |
| 239 local_prefs->ClearPref(prefs::kVariationsSeed); | |
| 240 return false; | |
| 241 } | |
| 242 return true; | |
| 243 } | |
| 244 | |
| 245 void VariationsService::CreateTrialFromStudy( | |
| 246 const chrome_variations::Study& study) { | |
| 247 LOG(INFO) << study.name(); | |
| 248 if (!ShouldAddStudy(study)){ | |
| 249 LOG(INFO) << "study rejected"; | |
| 250 return; | |
| 251 } | |
| 252 // At the moment, a missing default_experiment_name makes the study invalid. | |
| 253 if (!study.has_default_experiment_name()){ | |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put a space before {.
jwd
2012/05/08 03:35:18
Done.
| |
| 254 DVLOG(1) << study.name() << " has no default experiment defined."; | |
| 255 return; | |
| 256 } | |
| 257 LOG(INFO) << "study accepted"; | |
| 258 | |
| 259 const std::string& default_group_name = study.default_experiment_name(); | |
| 260 base::FieldTrial::Probability divisor = 0; | |
| 261 | |
| 262 bool found_default_group = false; | |
| 263 LOG(INFO) << "looping through groups " << study.experiment_size(); | |
| 264 LOG(INFO) << "looking for " << default_group_name; | |
| 265 int default_group_number; | |
| 266 bool has_default_group_number; | |
|
MAD
2012/05/08 03:09:00
Please initialize these two variables in case we d
| |
| 267 for (int i = 0; i<study.experiment_size(); ++i) { | |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put spaces around the <.
jwd
2012/05/08 03:35:18
Done.
| |
| 268 LOG(INFO) << study.experiment(i).name(); | |
| 269 divisor += study.experiment(i).probability_weight(); | |
| 270 if (study.experiment(i).name() == default_group_name){ | |
| 271 LOG(INFO) << "default found " << default_group_name; | |
| 272 found_default_group = true; | |
| 273 has_default_group_number = study.experiment(i).has_experiment_id(); | |
| 274 if (has_default_group_number) | |
| 275 default_group_number = study.experiment(i).experiment_id(); | |
|
MAD
2012/05/08 03:09:00
These are not related, the experiment_id, is the G
jwd
2012/05/08 03:30:01
Ya, I think I was confused about this when I wrote
Alexei Svitkine (slow)
2012/05/08 03:31:27
That parameter is an "out" parameter, so no need t
jwd
2012/05/08 03:35:18
Ya, I was very confused when I wrote that.
| |
| 276 } | |
| 277 } | |
| 278 if (!found_default_group){ | |
| 279 LOG(INFO) << study.name() << " is missing default experiment from " | |
| 280 << "experiment list"; | |
| 281 // The default group was not found in the list of groups. This study is not | |
| 282 // valid. | |
| 283 return; | |
| 284 } | |
| 285 | |
| 286 const base::Time epoch = base::Time::UnixEpoch(); | |
| 287 const base::Time expiry_date = | |
| 288 epoch + base::TimeDelta::FromSeconds(study.expiry_date()); | |
| 289 base::Time::Exploded exploded_end_date; | |
| 290 expiry_date.UTCExplode(&exploded_end_date); | |
| 291 | |
| 292 scoped_refptr<base::FieldTrial> trial( | |
| 293 base::FieldTrialList::FactoryGetFieldTrial( | |
| 294 study.name(), divisor, default_group_name, exploded_end_date.year, | |
| 295 exploded_end_date.month, exploded_end_date.day_of_month, | |
| 296 has_default_group_number ? &default_group_number : NULL)); | |
|
MAD
2012/05/08 03:09:00
This is not needed, you don't use it, and you won'
jwd
2012/05/08 03:30:01
Ya, got rid of this now.
Done.
| |
| 297 | |
| 298 if (study.has_consistency() && | |
| 299 study.consistency()==chrome_variations::Study_Consistency_PERMANENT) | |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put spaces around the ==.
jwd
2012/05/08 03:35:18
Done.
| |
| 300 trial->UseOneTimeRandomization(); | |
| 301 | |
| 302 for (int i = 0; i<study.experiment_size(); ++i) | |
|
Alexei Svitkine (slow)
2012/05/08 03:22:54
Put spaces around the <.
jwd
2012/05/08 03:35:18
Done.
| |
| 303 if (study.experiment(i).name() != default_group_name) | |
|
MAD
2012/05/08 03:09:00
Please use {} (for both the for and the if) when t
jwd
2012/05/08 03:35:18
Done.
| |
| 304 trial->AppendGroup(study.experiment(i).name(), | |
| 305 study.experiment(i).probability_weight()); | |
|
Alexei Svitkine (slow)
2012/05/08 03:25:26
You also need to associate the study.experiment(i)
jwd
2012/05/08 03:35:18
MAD says that that's not important for M20, but ye
| |
| 306 | |
| 307 trial->SetForced(); | |
| 308 } | |
| OLD | NEW |