Revert "Reland c91b178b07b0d - Delete dead signin code (SigninGlobalError)"
[chromium-blink-merge.git] / chrome / browser / metrics / variations / variations_service.cc
blob4ab913422fa7e1d5d5f9196443292fae01bb377c
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
3 // found in the LICENSE file.
5 #include "chrome/browser/metrics/variations/variations_service.h"
7 #include "base/build_time.h"
8 #include "base/command_line.h"
9 #include "base/metrics/histogram.h"
10 #include "base/metrics/sparse_histogram.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/sys_info.h"
14 #include "base/task_runner_util.h"
15 #include "base/timer/elapsed_timer.h"
16 #include "base/values.h"
17 #include "base/version.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/metrics/variations/generated_resources_map.h"
20 #include "chrome/common/channel_info.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "components/metrics/metrics_state_manager.h"
23 #include "components/network_time/network_time_tracker.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "components/variations/pref_names.h"
26 #include "components/variations/proto/variations_seed.pb.h"
27 #include "components/variations/variations_seed_processor.h"
28 #include "components/variations/variations_seed_simulator.h"
29 #include "components/variations/variations_url_constants.h"
30 #include "components/version_info/version_info.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/base/load_flags.h"
33 #include "net/base/net_errors.h"
34 #include "net/base/network_change_notifier.h"
35 #include "net/base/url_util.h"
36 #include "net/http/http_response_headers.h"
37 #include "net/http/http_status_code.h"
38 #include "net/http/http_util.h"
39 #include "net/url_request/url_fetcher.h"
40 #include "net/url_request/url_request_status.h"
41 #include "ui/base/device_form_factor.h"
42 #include "ui/base/resource/resource_bundle.h"
43 #include "url/gurl.h"
45 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
46 #include "chrome/browser/upgrade_detector_impl.h"
47 #endif
49 #if defined(OS_CHROMEOS)
50 #include "chrome/browser/chromeos/settings/cros_settings.h"
51 #endif
53 namespace chrome_variations {
55 namespace {
57 const int kMaxRetrySeedFetch = 5;
59 // TODO(mad): To be removed when we stop updating the NetworkTimeTracker.
60 // For the HTTP date headers, the resolution of the server time is 1 second.
61 const int64 kServerTimeResolutionMs = 1000;
63 // Wrapper around channel checking, used to enable channel mocking for
64 // testing. If the current browser channel is not UNKNOWN, this will return
65 // that channel value. Otherwise, if the fake channel flag is provided, this
66 // will return the fake channel. Failing that, this will return the UNKNOWN
67 // channel.
68 variations::Study_Channel GetChannelForVariations() {
69 switch (chrome::GetChannel()) {
70 case version_info::Channel::CANARY:
71 return variations::Study_Channel_CANARY;
72 case version_info::Channel::DEV:
73 return variations::Study_Channel_DEV;
74 case version_info::Channel::BETA:
75 return variations::Study_Channel_BETA;
76 case version_info::Channel::STABLE:
77 return variations::Study_Channel_STABLE;
78 case version_info::Channel::UNKNOWN:
79 break;
81 const std::string forced_channel =
82 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
83 switches::kFakeVariationsChannel);
84 if (forced_channel == "stable")
85 return variations::Study_Channel_STABLE;
86 if (forced_channel == "beta")
87 return variations::Study_Channel_BETA;
88 if (forced_channel == "dev")
89 return variations::Study_Channel_DEV;
90 if (forced_channel == "canary")
91 return variations::Study_Channel_CANARY;
92 DVLOG(1) << "Invalid channel provided: " << forced_channel;
93 return variations::Study_Channel_UNKNOWN;
96 // Returns a string that will be used for the value of the 'osname' URL param
97 // to the variations server.
98 std::string GetPlatformString() {
99 #if defined(OS_WIN)
100 return "win";
101 #elif defined(OS_IOS)
102 return "ios";
103 #elif defined(OS_MACOSX)
104 return "mac";
105 #elif defined(OS_CHROMEOS)
106 return "chromeos";
107 #elif defined(OS_ANDROID)
108 return "android";
109 #elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
110 // Default BSD and SOLARIS to Linux to not break those builds, although these
111 // platforms are not officially supported by Chrome.
112 return "linux";
113 #else
114 #error Unknown platform
115 #endif
118 // Gets the version number to use for variations seed simulation. Must be called
119 // on a thread where IO is allowed.
120 base::Version GetVersionForSimulation() {
121 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
122 const base::Version installed_version =
123 UpgradeDetectorImpl::GetCurrentlyInstalledVersion();
124 if (installed_version.IsValid())
125 return installed_version;
126 #endif // !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
128 // TODO(asvitkine): Get the version that will be used on restart instead of
129 // the current version on Android, iOS and ChromeOS.
130 return base::Version(version_info::GetVersionNumber());
133 // Gets the restrict parameter from |policy_pref_service| or from Chrome OS
134 // settings in the case of that platform.
135 std::string GetRestrictParameterPref(PrefService* policy_pref_service) {
136 std::string parameter;
137 #if defined(OS_CHROMEOS)
138 chromeos::CrosSettings::Get()->GetString(
139 chromeos::kVariationsRestrictParameter, &parameter);
140 #else
141 if (policy_pref_service) {
142 parameter =
143 policy_pref_service->GetString(prefs::kVariationsRestrictParameter);
145 #endif
146 return parameter;
149 enum ResourceRequestsAllowedState {
150 RESOURCE_REQUESTS_ALLOWED,
151 RESOURCE_REQUESTS_NOT_ALLOWED,
152 RESOURCE_REQUESTS_ALLOWED_NOTIFIED,
153 RESOURCE_REQUESTS_NOT_ALLOWED_EULA_NOT_ACCEPTED,
154 RESOURCE_REQUESTS_NOT_ALLOWED_NETWORK_DOWN,
155 RESOURCE_REQUESTS_NOT_ALLOWED_COMMAND_LINE_DISABLED,
156 RESOURCE_REQUESTS_ALLOWED_ENUM_SIZE,
159 // Records UMA histogram with the current resource requests allowed state.
160 void RecordRequestsAllowedHistogram(ResourceRequestsAllowedState state) {
161 UMA_HISTOGRAM_ENUMERATION("Variations.ResourceRequestsAllowed", state,
162 RESOURCE_REQUESTS_ALLOWED_ENUM_SIZE);
165 // Converts ResourceRequestAllowedNotifier::State to the corresponding
166 // ResourceRequestsAllowedState value.
167 ResourceRequestsAllowedState ResourceRequestStateToHistogramValue(
168 web_resource::ResourceRequestAllowedNotifier::State state) {
169 using web_resource::ResourceRequestAllowedNotifier;
170 switch (state) {
171 case ResourceRequestAllowedNotifier::DISALLOWED_EULA_NOT_ACCEPTED:
172 return RESOURCE_REQUESTS_NOT_ALLOWED_EULA_NOT_ACCEPTED;
173 case ResourceRequestAllowedNotifier::DISALLOWED_NETWORK_DOWN:
174 return RESOURCE_REQUESTS_NOT_ALLOWED_NETWORK_DOWN;
175 case ResourceRequestAllowedNotifier::DISALLOWED_COMMAND_LINE_DISABLED:
176 return RESOURCE_REQUESTS_NOT_ALLOWED_COMMAND_LINE_DISABLED;
177 case ResourceRequestAllowedNotifier::ALLOWED:
178 return RESOURCE_REQUESTS_ALLOWED;
180 NOTREACHED();
181 return RESOURCE_REQUESTS_NOT_ALLOWED;
185 // Gets current form factor and converts it from enum DeviceFormFactor to enum
186 // Study_FormFactor.
187 variations::Study_FormFactor GetCurrentFormFactor() {
188 switch (ui::GetDeviceFormFactor()) {
189 case ui::DEVICE_FORM_FACTOR_PHONE:
190 return variations::Study_FormFactor_PHONE;
191 case ui::DEVICE_FORM_FACTOR_TABLET:
192 return variations::Study_FormFactor_TABLET;
193 case ui::DEVICE_FORM_FACTOR_DESKTOP:
194 return variations::Study_FormFactor_DESKTOP;
196 NOTREACHED();
197 return variations::Study_FormFactor_DESKTOP;
200 // Gets the hardware class and returns it as a string. This returns an empty
201 // string if the client is not ChromeOS.
202 std::string GetHardwareClass() {
203 #if defined(OS_CHROMEOS)
204 return base::SysInfo::GetLsbReleaseBoard();
205 #endif // OS_CHROMEOS
206 return std::string();
209 // Returns the date that should be used by the VariationsSeedProcessor to do
210 // expiry and start date checks.
211 base::Time GetReferenceDateForExpiryChecks(PrefService* local_state) {
212 const int64 date_value = local_state->GetInt64(prefs::kVariationsSeedDate);
213 const base::Time seed_date = base::Time::FromInternalValue(date_value);
214 const base::Time build_time = base::GetBuildTime();
215 // Use the build time for date checks if either the seed date is invalid or
216 // the build time is newer than the seed date.
217 base::Time reference_date = seed_date;
218 if (seed_date.is_null() || seed_date < build_time)
219 reference_date = build_time;
220 return reference_date;
223 // Returns the header value for |name| from |headers| or an empty string if not
224 // set.
225 std::string GetHeaderValue(const net::HttpResponseHeaders* headers,
226 const base::StringPiece& name) {
227 std::string value;
228 headers->EnumerateHeader(NULL, name, &value);
229 return value;
232 // Overrides the string resource sepecified by |hash| with |string| in the
233 // resource bundle. Used as a callback passed to the variations seed processor.
234 void OverrideUIString(uint32_t hash, const base::string16& string) {
235 int resource_id = GetResourceIndex(hash);
236 if (resource_id == -1)
237 return;
239 ui::ResourceBundle::GetSharedInstance().OverrideLocaleStringResource(
240 resource_id, string);
243 } // namespace
245 VariationsService::VariationsService(
246 scoped_ptr<VariationsServiceClient> client,
247 web_resource::ResourceRequestAllowedNotifier* notifier,
248 PrefService* local_state,
249 metrics::MetricsStateManager* state_manager)
250 : client_(client.Pass()),
251 local_state_(local_state),
252 state_manager_(state_manager),
253 policy_pref_service_(local_state),
254 seed_store_(local_state),
255 create_trials_from_seed_called_(false),
256 initial_request_completed_(false),
257 disable_deltas_for_next_request_(false),
258 resource_request_allowed_notifier_(notifier),
259 request_count_(0),
260 weak_ptr_factory_(this) {
261 resource_request_allowed_notifier_->Init(this);
264 VariationsService::~VariationsService() {
267 bool VariationsService::CreateTrialsFromSeed() {
268 DCHECK(thread_checker_.CalledOnValidThread());
270 create_trials_from_seed_called_ = true;
272 variations::VariationsSeed seed;
273 if (!seed_store_.LoadSeed(&seed))
274 return false;
276 const base::Version current_version(version_info::GetVersionNumber());
277 if (!current_version.IsValid())
278 return false;
280 variations::Study_Channel channel = GetChannelForVariations();
281 UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.UserChannel", channel);
283 const std::string latest_country =
284 local_state_->GetString(prefs::kVariationsCountry);
285 variations::VariationsSeedProcessor().CreateTrialsFromSeed(
286 seed, client_->GetApplicationLocale(),
287 GetReferenceDateForExpiryChecks(local_state_), current_version, channel,
288 GetCurrentFormFactor(), GetHardwareClass(), latest_country,
289 LoadPermanentConsistencyCountry(current_version, latest_country),
290 base::Bind(&OverrideUIString));
292 const base::Time now = base::Time::Now();
294 // Log the "freshness" of the seed that was just used. The freshness is the
295 // time between the last successful seed download and now.
296 const int64 last_fetch_time_internal =
297 local_state_->GetInt64(prefs::kVariationsLastFetchTime);
298 if (last_fetch_time_internal) {
299 const base::TimeDelta delta =
300 now - base::Time::FromInternalValue(last_fetch_time_internal);
301 // Log the value in number of minutes.
302 UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.SeedFreshness", delta.InMinutes(),
303 1, base::TimeDelta::FromDays(30).InMinutes(), 50);
306 // Log the skew between the seed date and the system clock/build time to
307 // analyze whether either could be used to make old variations seeds expire
308 // after some time.
309 const int64 seed_date_internal =
310 local_state_->GetInt64(prefs::kVariationsSeedDate);
311 if (seed_date_internal) {
312 const base::Time seed_date =
313 base::Time::FromInternalValue(seed_date_internal);
314 const int system_clock_delta_days = (now - seed_date).InDays();
315 if (system_clock_delta_days < 0) {
316 UMA_HISTOGRAM_COUNTS_100("Variations.SeedDateSkew.SystemClockBehindBy",
317 -system_clock_delta_days);
318 } else {
319 UMA_HISTOGRAM_COUNTS_100("Variations.SeedDateSkew.SystemClockAheadBy",
320 system_clock_delta_days);
323 const int build_time_delta_days =
324 (base::GetBuildTime() - seed_date).InDays();
325 if (build_time_delta_days < 0) {
326 UMA_HISTOGRAM_COUNTS_100("Variations.SeedDateSkew.BuildTimeBehindBy",
327 -build_time_delta_days);
328 } else {
329 UMA_HISTOGRAM_COUNTS_100("Variations.SeedDateSkew.BuildTimeAheadBy",
330 build_time_delta_days);
334 return true;
337 void VariationsService::StartRepeatedVariationsSeedFetch() {
338 DCHECK(thread_checker_.CalledOnValidThread());
340 // Initialize the Variations server URL.
341 variations_server_url_ =
342 GetVariationsServerURL(policy_pref_service_, restrict_mode_);
344 // Check that |CreateTrialsFromSeed| was called, which is necessary to
345 // retrieve the serial number that will be sent to the server.
346 DCHECK(create_trials_from_seed_called_);
348 DCHECK(!request_scheduler_.get());
349 // Note that the act of instantiating the scheduler will start the fetch, if
350 // the scheduler deems appropriate.
351 request_scheduler_.reset(VariationsRequestScheduler::Create(
352 base::Bind(&VariationsService::FetchVariationsSeed,
353 weak_ptr_factory_.GetWeakPtr()),
354 local_state_));
355 request_scheduler_->Start();
358 void VariationsService::AddObserver(Observer* observer) {
359 DCHECK(thread_checker_.CalledOnValidThread());
360 observer_list_.AddObserver(observer);
363 void VariationsService::RemoveObserver(Observer* observer) {
364 DCHECK(thread_checker_.CalledOnValidThread());
365 observer_list_.RemoveObserver(observer);
368 void VariationsService::OnAppEnterForeground() {
369 DCHECK(thread_checker_.CalledOnValidThread());
371 // On mobile platforms, initialize the fetch scheduler when we receive the
372 // first app foreground notification.
373 if (!request_scheduler_)
374 StartRepeatedVariationsSeedFetch();
375 request_scheduler_->OnAppEnterForeground();
378 #if defined(OS_WIN)
379 void VariationsService::StartGoogleUpdateRegistrySync() {
380 DCHECK(thread_checker_.CalledOnValidThread());
381 registry_syncer_.RequestRegistrySync();
383 #endif
385 void VariationsService::SetRestrictMode(const std::string& restrict_mode) {
386 DCHECK(thread_checker_.CalledOnValidThread());
388 // This should be called before the server URL has been computed.
389 DCHECK(variations_server_url_.is_empty());
390 restrict_mode_ = restrict_mode;
393 void VariationsService::SetCreateTrialsFromSeedCalledForTesting(bool called) {
394 DCHECK(thread_checker_.CalledOnValidThread());
395 create_trials_from_seed_called_ = called;
398 // static
399 GURL VariationsService::GetVariationsServerURL(
400 PrefService* policy_pref_service,
401 const std::string& restrict_mode_override) {
402 std::string server_url_string(
403 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
404 switches::kVariationsServerURL));
405 if (server_url_string.empty())
406 server_url_string = kDefaultServerUrl;
407 GURL server_url = GURL(server_url_string);
409 const std::string restrict_param = !restrict_mode_override.empty() ?
410 restrict_mode_override : GetRestrictParameterPref(policy_pref_service);
411 if (!restrict_param.empty()) {
412 server_url = net::AppendOrReplaceQueryParameter(server_url,
413 "restrict",
414 restrict_param);
417 server_url = net::AppendOrReplaceQueryParameter(server_url, "osname",
418 GetPlatformString());
420 DCHECK(server_url.is_valid());
421 return server_url;
424 // static
425 std::string VariationsService::GetDefaultVariationsServerURLForTesting() {
426 return kDefaultServerUrl;
429 // static
430 void VariationsService::RegisterPrefs(PrefRegistrySimple* registry) {
431 VariationsSeedStore::RegisterPrefs(registry);
432 registry->RegisterInt64Pref(prefs::kVariationsLastFetchTime, 0);
433 // This preference will only be written by the policy service, which will fill
434 // it according to a value stored in the User Policy.
435 registry->RegisterStringPref(prefs::kVariationsRestrictParameter,
436 std::string());
437 // This preference keeps track of the country code used to filter
438 // permanent-consistency studies.
439 registry->RegisterListPref(prefs::kVariationsPermanentConsistencyCountry);
442 // static
443 void VariationsService::RegisterProfilePrefs(
444 user_prefs::PrefRegistrySyncable* registry) {
445 // This preference will only be written by the policy service, which will fill
446 // it according to a value stored in the User Policy.
447 registry->RegisterStringPref(prefs::kVariationsRestrictParameter,
448 std::string());
451 // static
452 scoped_ptr<VariationsService> VariationsService::Create(
453 scoped_ptr<VariationsServiceClient> client,
454 PrefService* local_state,
455 metrics::MetricsStateManager* state_manager) {
456 scoped_ptr<VariationsService> result;
457 #if !defined(GOOGLE_CHROME_BUILD)
458 // Unless the URL was provided, unsupported builds should return NULL to
459 // indicate that the service should not be used.
460 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
461 switches::kVariationsServerURL)) {
462 DVLOG(1) << "Not creating VariationsService in unofficial build without --"
463 << switches::kVariationsServerURL << " specified.";
464 return result.Pass();
466 #endif
467 result.reset(new VariationsService(
468 client.Pass(), new web_resource::ResourceRequestAllowedNotifier(
469 local_state, switches::kDisableBackgroundNetworking),
470 local_state, state_manager));
471 return result.Pass();
474 void VariationsService::DoActualFetch() {
475 DCHECK(thread_checker_.CalledOnValidThread());
476 DCHECK(!pending_seed_request_);
478 pending_seed_request_ = net::URLFetcher::Create(0, variations_server_url_,
479 net::URLFetcher::GET, this);
480 pending_seed_request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
481 net::LOAD_DO_NOT_SAVE_COOKIES);
482 pending_seed_request_->SetRequestContext(
483 g_browser_process->system_request_context());
484 pending_seed_request_->SetMaxRetriesOn5xx(kMaxRetrySeedFetch);
485 if (!seed_store_.variations_serial_number().empty() &&
486 !disable_deltas_for_next_request_) {
487 // If the current seed includes a country code, deltas are not supported (as
488 // the serial number doesn't take into account the country code). The server
489 // will update us with a seed that doesn't include a country code which will
490 // enable deltas to work.
491 // TODO(asvitkine): Remove the check in M50+ when the percentage of clients
492 // that have an old seed with a country code becomes miniscule.
493 if (!seed_store_.seed_has_country_code()) {
494 // Tell the server that delta-compressed seeds are supported.
495 pending_seed_request_->AddExtraRequestHeader("A-IM:x-bm");
497 // Get the seed only if its serial number doesn't match what we have.
498 pending_seed_request_->AddExtraRequestHeader(
499 "If-None-Match:" + seed_store_.variations_serial_number());
501 pending_seed_request_->Start();
503 const base::TimeTicks now = base::TimeTicks::Now();
504 base::TimeDelta time_since_last_fetch;
505 // Record a time delta of 0 (default value) if there was no previous fetch.
506 if (!last_request_started_time_.is_null())
507 time_since_last_fetch = now - last_request_started_time_;
508 UMA_HISTOGRAM_CUSTOM_COUNTS("Variations.TimeSinceLastFetchAttempt",
509 time_since_last_fetch.InMinutes(), 0,
510 base::TimeDelta::FromDays(7).InMinutes(), 50);
511 UMA_HISTOGRAM_COUNTS_100("Variations.RequestCount", request_count_);
512 ++request_count_;
513 last_request_started_time_ = now;
514 disable_deltas_for_next_request_ = false;
517 bool VariationsService::StoreSeed(const std::string& seed_data,
518 const std::string& seed_signature,
519 const std::string& country_code,
520 const base::Time& date_fetched,
521 bool is_delta_compressed) {
522 DCHECK(thread_checker_.CalledOnValidThread());
524 scoped_ptr<variations::VariationsSeed> seed(new variations::VariationsSeed);
525 if (!seed_store_.StoreSeedData(seed_data, seed_signature, country_code,
526 date_fetched, is_delta_compressed,
527 seed.get())) {
528 return false;
530 RecordLastFetchTime();
532 // Perform seed simulation only if |state_manager_| is not-NULL. The state
533 // manager may be NULL for some unit tests.
534 if (!state_manager_)
535 return true;
537 base::PostTaskAndReplyWithResult(
538 content::BrowserThread::GetBlockingPool(),
539 FROM_HERE,
540 base::Bind(&GetVersionForSimulation),
541 base::Bind(&VariationsService::PerformSimulationWithVersion,
542 weak_ptr_factory_.GetWeakPtr(), base::Passed(&seed)));
543 return true;
546 void VariationsService::FetchVariationsSeed() {
547 DCHECK(thread_checker_.CalledOnValidThread());
549 const web_resource::ResourceRequestAllowedNotifier::State state =
550 resource_request_allowed_notifier_->GetResourceRequestsAllowedState();
551 RecordRequestsAllowedHistogram(ResourceRequestStateToHistogramValue(state));
552 if (state != web_resource::ResourceRequestAllowedNotifier::ALLOWED) {
553 DVLOG(1) << "Resource requests were not allowed. Waiting for notification.";
554 return;
557 DoActualFetch();
560 void VariationsService::NotifyObservers(
561 const variations::VariationsSeedSimulator::Result& result) {
562 DCHECK(thread_checker_.CalledOnValidThread());
564 if (result.kill_critical_group_change_count > 0) {
565 FOR_EACH_OBSERVER(Observer, observer_list_,
566 OnExperimentChangesDetected(Observer::CRITICAL));
567 } else if (result.kill_best_effort_group_change_count > 0) {
568 FOR_EACH_OBSERVER(Observer, observer_list_,
569 OnExperimentChangesDetected(Observer::BEST_EFFORT));
573 void VariationsService::OnURLFetchComplete(const net::URLFetcher* source) {
574 DCHECK(thread_checker_.CalledOnValidThread());
575 DCHECK_EQ(pending_seed_request_.get(), source);
577 const bool is_first_request = !initial_request_completed_;
578 initial_request_completed_ = true;
580 // The fetcher will be deleted when the request is handled.
581 scoped_ptr<const net::URLFetcher> request(pending_seed_request_.release());
582 const net::URLRequestStatus& request_status = request->GetStatus();
583 if (request_status.status() != net::URLRequestStatus::SUCCESS) {
584 UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.FailedRequestErrorCode",
585 -request_status.error());
586 DVLOG(1) << "Variations server request failed with error: "
587 << request_status.error() << ": "
588 << net::ErrorToString(request_status.error());
589 // It's common for the very first fetch attempt to fail (e.g. the network
590 // may not yet be available). In such a case, try again soon, rather than
591 // waiting the full time interval.
592 if (is_first_request)
593 request_scheduler_->ScheduleFetchShortly();
594 return;
597 // Log the response code.
598 const int response_code = request->GetResponseCode();
599 UMA_HISTOGRAM_SPARSE_SLOWLY("Variations.SeedFetchResponseCode",
600 response_code);
602 const base::TimeDelta latency =
603 base::TimeTicks::Now() - last_request_started_time_;
605 base::Time response_date;
606 if (response_code == net::HTTP_OK ||
607 response_code == net::HTTP_NOT_MODIFIED) {
608 bool success = request->GetResponseHeaders()->GetDateValue(&response_date);
609 DCHECK(success || response_date.is_null());
611 if (!response_date.is_null()) {
612 g_browser_process->network_time_tracker()->UpdateNetworkTime(
613 response_date,
614 base::TimeDelta::FromMilliseconds(kServerTimeResolutionMs),
615 latency,
616 base::TimeTicks::Now());
620 if (response_code != net::HTTP_OK) {
621 DVLOG(1) << "Variations server request returned non-HTTP_OK response code: "
622 << response_code;
623 if (response_code == net::HTTP_NOT_MODIFIED) {
624 RecordLastFetchTime();
625 // Update the seed date value in local state (used for expiry check on
626 // next start up), since 304 is a successful response.
627 seed_store_.UpdateSeedDateAndLogDayChange(response_date);
629 return;
632 std::string seed_data;
633 bool success = request->GetResponseAsString(&seed_data);
634 DCHECK(success);
636 net::HttpResponseHeaders* headers = request->GetResponseHeaders();
637 const std::string signature = GetHeaderValue(headers, "X-Seed-Signature");
638 const std::string country_code = GetHeaderValue(headers, "X-Country");
639 const bool is_delta_compressed = (GetHeaderValue(headers, "IM") == "x-bm");
640 const bool store_success = StoreSeed(seed_data, signature, country_code,
641 response_date, is_delta_compressed);
642 if (!store_success && is_delta_compressed) {
643 disable_deltas_for_next_request_ = true;
644 request_scheduler_->ScheduleFetchShortly();
648 void VariationsService::OnResourceRequestsAllowed() {
649 DCHECK(thread_checker_.CalledOnValidThread());
651 // Note that this only attempts to fetch the seed at most once per period
652 // (kSeedFetchPeriodHours). This works because
653 // |resource_request_allowed_notifier_| only calls this method if an
654 // attempt was made earlier that fails (which implies that the period had
655 // elapsed). After a successful attempt is made, the notifier will know not
656 // to call this method again until another failed attempt occurs.
657 RecordRequestsAllowedHistogram(RESOURCE_REQUESTS_ALLOWED_NOTIFIED);
658 DVLOG(1) << "Retrying fetch.";
659 DoActualFetch();
661 // This service must have created a scheduler in order for this to be called.
662 DCHECK(request_scheduler_.get());
663 request_scheduler_->Reset();
666 void VariationsService::PerformSimulationWithVersion(
667 scoped_ptr<variations::VariationsSeed> seed,
668 const base::Version& version) {
669 DCHECK(thread_checker_.CalledOnValidThread());
671 if (!version.IsValid())
672 return;
674 const base::ElapsedTimer timer;
676 scoped_ptr<const base::FieldTrial::EntropyProvider> entropy_provider =
677 state_manager_->CreateEntropyProvider();
678 variations::VariationsSeedSimulator seed_simulator(*entropy_provider);
680 const std::string latest_country =
681 local_state_->GetString(prefs::kVariationsCountry);
682 const variations::VariationsSeedSimulator::Result result =
683 seed_simulator.SimulateSeedStudies(
684 *seed, client_->GetApplicationLocale(),
685 GetReferenceDateForExpiryChecks(local_state_), version,
686 GetChannelForVariations(), GetCurrentFormFactor(), GetHardwareClass(),
687 latest_country,
688 LoadPermanentConsistencyCountry(version, latest_country));
690 UMA_HISTOGRAM_COUNTS_100("Variations.SimulateSeed.NormalChanges",
691 result.normal_group_change_count);
692 UMA_HISTOGRAM_COUNTS_100("Variations.SimulateSeed.KillBestEffortChanges",
693 result.kill_best_effort_group_change_count);
694 UMA_HISTOGRAM_COUNTS_100("Variations.SimulateSeed.KillCriticalChanges",
695 result.kill_critical_group_change_count);
697 UMA_HISTOGRAM_TIMES("Variations.SimulateSeed.Duration", timer.Elapsed());
699 NotifyObservers(result);
702 void VariationsService::RecordLastFetchTime() {
703 DCHECK(thread_checker_.CalledOnValidThread());
705 // local_state_ is NULL in tests, so check it first.
706 if (local_state_) {
707 local_state_->SetInt64(prefs::kVariationsLastFetchTime,
708 base::Time::Now().ToInternalValue());
712 std::string VariationsService::GetInvalidVariationsSeedSignature() const {
713 DCHECK(thread_checker_.CalledOnValidThread());
714 return seed_store_.GetInvalidSignature();
717 std::string VariationsService::LoadPermanentConsistencyCountry(
718 const base::Version& version,
719 const std::string& latest_country) {
720 DCHECK(thread_checker_.CalledOnValidThread());
721 DCHECK(version.IsValid());
723 const base::ListValue* list_value =
724 local_state_->GetList(prefs::kVariationsPermanentConsistencyCountry);
725 std::string stored_version_string;
726 std::string stored_country;
728 // Determine if the saved pref value is present and valid.
729 const bool is_pref_present = list_value && !list_value->empty();
730 const bool is_pref_valid = is_pref_present && list_value->GetSize() == 2 &&
731 list_value->GetString(0, &stored_version_string) &&
732 list_value->GetString(1, &stored_country) &&
733 base::Version(stored_version_string).IsValid();
735 // Determine if the version from the saved pref matches |version|.
736 const bool does_version_match =
737 is_pref_valid && version.Equals(base::Version(stored_version_string));
739 // Determine if the country in the saved pref matches the country in |seed|.
740 const bool does_country_match = is_pref_valid && !latest_country.empty() &&
741 stored_country == latest_country;
743 // Record a histogram for how the saved pref value compares to the current
744 // version and the country code in the variations seed.
745 LoadPermanentConsistencyCountryResult result;
746 if (!is_pref_present) {
747 result = !latest_country.empty() ? LOAD_COUNTRY_NO_PREF_HAS_SEED
748 : LOAD_COUNTRY_NO_PREF_NO_SEED;
749 } else if (!is_pref_valid) {
750 result = !latest_country.empty() ? LOAD_COUNTRY_INVALID_PREF_HAS_SEED
751 : LOAD_COUNTRY_INVALID_PREF_NO_SEED;
752 } else if (latest_country.empty()) {
753 result = does_version_match ? LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_EQ
754 : LOAD_COUNTRY_HAS_PREF_NO_SEED_VERSION_NEQ;
755 } else if (does_version_match) {
756 result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_EQ
757 : LOAD_COUNTRY_HAS_BOTH_VERSION_EQ_COUNTRY_NEQ;
758 } else {
759 result = does_country_match ? LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_EQ
760 : LOAD_COUNTRY_HAS_BOTH_VERSION_NEQ_COUNTRY_NEQ;
762 UMA_HISTOGRAM_ENUMERATION("Variations.LoadPermanentConsistencyCountryResult",
763 result, LOAD_COUNTRY_MAX);
765 // Use the stored country if one is available and was fetched since the last
766 // time Chrome was updated.
767 if (is_pref_present && does_version_match)
768 return stored_country;
770 if (latest_country.empty()) {
771 if (!is_pref_valid)
772 local_state_->ClearPref(prefs::kVariationsPermanentConsistencyCountry);
773 // If we've never received a country code from the server, use an empty
774 // country so that it won't pass any filters that specifically include
775 // countries, but so that it will pass any filters that specifically exclude
776 // countries.
777 return std::string();
780 // Otherwise, update the pref with the current Chrome version and country.
781 base::ListValue new_list_value;
782 new_list_value.AppendString(version.GetString());
783 new_list_value.AppendString(latest_country);
784 local_state_->Set(prefs::kVariationsPermanentConsistencyCountry,
785 new_list_value);
786 return latest_country;
789 } // namespace chrome_variations