Index: components/rappor/rappor_service.cc |
diff --git a/components/rappor/rappor_service.cc b/components/rappor/rappor_service.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..855f49f8dcfd2dc4ec8e9a9c80dafb61ac3f02bd |
--- /dev/null |
+++ b/components/rappor/rappor_service.cc |
@@ -0,0 +1,167 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/rappor/rappor_service.h" |
+ |
+#include <cstdlib> |
+ |
+#include "base/base64.h" |
+#include "base/hash.h" |
+#include "base/metrics/field_trial.h" |
+#include "base/prefs/pref_registry_simple.h" |
+#include "base/prefs/pref_service.h" |
+#include "base/rand_util.h" |
+#include "base/stl_util.h" |
+#include "components/rappor/proto/rappor_metric.pb.h" |
+#include "components/rappor/rappor_pref_names.h" |
+ |
+namespace { |
+ |
+// Seconds before the initial log is generated. |
+const int kInitialLogIntervalSeconds = 15; |
+// Interval between ongoing logs. |
+const int kLogIntervalSeconds = 30 * 60; |
+ |
+const char kServerUrl[] = "https://clients4.google.com/rappor/v2"; |
Alexei Svitkine (slow)
2014/01/21 18:14:04
Probably wouldn't be v2.
Maybe it's a good idea t
Steven Holte
2014/01/21 20:25:14
Done. Also changed service activation to depend o
|
+const char kMimeType[] = "application/vnd.chrome.rappor"; |
+ |
+// Constants for the RAPPOR rollout field trial. |
+const char kRapporRolloutFieldTrialName[] = "RapporRollout"; |
+const char kRapporRolloutFieldTrialEnabledGroupName[] = "Enabled"; |
+ |
+bool FieldTrialIsActive() { |
+ return base::FieldTrialList::FindFullName(kRapporRolloutFieldTrialName) == |
+ kRapporRolloutFieldTrialEnabledGroupName; |
+} |
+ |
+} // namespace |
+ |
+namespace rappor { |
+ |
+RapporService::RapporService() {} |
+ |
+RapporService::~RapporService() {} |
+ |
+void RapporService::Start(PrefService* pref_service, |
+ net::URLRequestContextGetter* request_context) { |
+ if (!FieldTrialIsActive()) |
+ return; |
+ DCHECK(!uploader_); |
+ GetSecret(pref_service); |
+ uploader_.reset(new LogUploader(kServerUrl, kMimeType, request_context)); |
+ log_rotation_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kInitialLogIntervalSeconds), |
+ this, |
+ &RapporService::OnLogInterval); |
+} |
+ |
+void RapporService::OnLogInterval() { |
+ DCHECK(uploader_); |
+ RapporReports reports; |
+ LogMetrics(&reports); |
+ if (reports.report_size() > 0) { |
+ std::string log_text; |
+ bool success = reports.SerializeToString(&log_text); |
+ DCHECK(success); |
+ uploader_->QueueLog(log_text); |
+ } |
+ log_rotation_timer_.Start(FROM_HERE, |
+ base::TimeDelta::FromSeconds(kLogIntervalSeconds), |
+ this, |
+ &RapporService::OnLogInterval); |
+} |
+ |
+// static |
+void RapporService::RegisterPrefs(PrefRegistrySimple* registry) { |
+ registry->RegisterStringPref(prefs::kRapporSecret, std::string()); |
+} |
+ |
+void RapporService::GetSecret(PrefService* pref_service) { |
+ if (!secret_.empty()) |
+ return; |
+ std::string secret_base64 = |
+ pref_service->GetString(prefs::kRapporSecret); |
+ if (!secret_base64.empty()) { |
+ bool decoded = base::Base64Decode(secret_base64, &secret_); |
+ if (decoded) |
+ return; |
+ // If the preference fails to decode, it must be corrupt, so continue as |
+ // though it didn't exist yet and generate a new one. |
+ } |
+ |
+ secret_ = base::RandBytesAsString(128); |
+ base::Base64Encode(secret_, &secret_base64); |
+ pref_service->SetString(prefs::kRapporSecret, secret_base64); |
+} |
+ |
+void RapporService::LogMetric(const RapporMetric& metric, |
+ RapporReports::Report* report) { |
+ report->set_name_hash(base::Hash(metric.parameters()->rappor_name)); |
+ ByteVector bytes = metric.GetReport(secret_); |
+ std::string byte_string(bytes.begin(), bytes.end()); |
+ report->set_bits(byte_string); |
+} |
+ |
+void RapporService::LogMetrics(RapporReports* reports) { |
+ base::AutoLock auto_lock(lock_); |
+ |
+ for (std::map<std::string, RapporMetric*>::iterator it = metrics_map_.begin(); |
+ metrics_map_.end() != it; |
+ ++it) { |
+ const RapporMetric* metric = it->second; |
+ DCHECK_EQ(it->first, metric->parameters()->rappor_name); |
+ RapporReports::Report* report = reports->add_report(); |
+ LogMetric(*metric, report); |
+ } |
+ STLDeleteContainerPairSecondPointers( |
+ metrics_map_.begin(), metrics_map_.end()); |
+ metrics_map_.clear(); |
+} |
+ |
+void RapporService::RecordSamples(const RapporParameters& parameters, |
+ const std::vector<std::string>& samples) { |
+ if (!FieldTrialIsActive()) |
+ return; |
+ RecordSamplesInternal(parameters, samples); |
+} |
+ |
+void RapporService::RecordUrl(const RapporParameters& parameters, |
+ const GURL& url) { |
+ if (!FieldTrialIsActive()) |
+ return; |
+ std::vector<std::string> samples; |
+ samples.push_back(url.spec()); |
+ samples.push_back(url.host()); |
+ // TODO(holte): break up host more |
+ samples.push_back(url.query()); |
+ samples.push_back(url.path()); |
+ RecordSamplesInternal(parameters, samples); |
+} |
+ |
+void RapporService::RecordSamplesInternal( |
+ const RapporParameters& parameters, |
+ const std::vector<std::string>& samples) { |
+ base::AutoLock auto_lock(lock_); |
+ |
+ RapporMetric* metric = LookupMetric(parameters); |
+ metric->AddSamples(samples); |
+} |
+ |
+RapporMetric* RapporService::LookupMetric( |
+ const RapporParameters& parameters) { |
+ std::map<std::string, RapporMetric*>::iterator it = |
+ metrics_map_.find(parameters.rappor_name); |
+ if (metrics_map_.end() != it) { |
+ RapporMetric* metric = it->second; |
+ DCHECK_EQ(parameters, *metric->parameters()); |
+ return metric; |
+ } |
+ |
+ RapporMetric* new_metric = new RapporMetric(parameters); |
+ metrics_map_[parameters.rappor_name] = new_metric; |
+ return new_metric; |
+} |
+ |
+} // namespace rappor |