// Copyright 2017 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/ukm/ukm_service.h" #include #include #include #include "base/bind.h" #include "base/feature_list.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" #include "components/metrics/metrics_log.h" #include "components/metrics/metrics_service_client.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/ukm/persisted_logs_metrics_impl.h" #include "components/ukm/ukm_pref_names.h" #include "components/ukm/ukm_rotation_scheduler.h" #include "services/metrics/public/cpp/delegating_ukm_recorder.h" #include "third_party/metrics_proto/ukm/report.pb.h" namespace ukm { namespace { // Generates a new client id and stores it in prefs. uint64_t GenerateClientId(PrefService* pref_service) { uint64_t client_id = 0; while (!client_id) client_id = base::RandUint64(); pref_service->SetInt64(prefs::kUkmClientId, client_id); // Also reset the session id counter. pref_service->SetInteger(prefs::kUkmSessionId, 0); return client_id; } uint64_t LoadOrGenerateClientId(PrefService* pref_service) { uint64_t client_id = pref_service->GetInt64(prefs::kUkmClientId); if (!client_id) client_id = GenerateClientId(pref_service); return client_id; } int32_t LoadSessionId(PrefService* pref_service) { int32_t session_id = pref_service->GetInteger(prefs::kUkmSessionId); ++session_id; // increment session id, once per session pref_service->SetInteger(prefs::kUkmSessionId, session_id); return session_id; } } // namespace UkmService::UkmService(PrefService* pref_service, metrics::MetricsServiceClient* client, bool restrict_to_whitelist_entries) : pref_service_(pref_service), restrict_to_whitelist_entries_(restrict_to_whitelist_entries), client_id_(0), session_id_(0), report_count_(0), client_(client), reporting_service_(client, pref_service), initialize_started_(false), initialize_complete_(false), self_ptr_factory_(this) { DCHECK(pref_service_); DCHECK(client_); DVLOG(1) << "UkmService::Constructor"; reporting_service_.Initialize(); base::Closure rotate_callback = base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr()); // MetricsServiceClient outlives UkmService, and // MetricsReportingScheduler is tied to the lifetime of |this|. const base::Callback& get_upload_interval_callback = base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval, base::Unretained(client_)); scheduler_.reset(new ukm::UkmRotationScheduler(rotate_callback, get_upload_interval_callback)); StoreWhitelistedEntries(); DelegatingUkmRecorder::Get()->AddDelegate(self_ptr_factory_.GetWeakPtr()); } UkmService::~UkmService() { DisableReporting(); DelegatingUkmRecorder::Get()->RemoveDelegate(this); } void UkmService::Initialize() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!initialize_started_); DVLOG(1) << "UkmService::Initialize"; initialize_started_ = true; DCHECK_EQ(0, report_count_); client_id_ = LoadOrGenerateClientId(pref_service_); session_id_ = LoadSessionId(pref_service_); metrics_providers_.Init(); StartInitTask(); } void UkmService::EnableReporting() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::EnableReporting"; if (reporting_service_.reporting_active()) return; metrics_providers_.OnRecordingEnabled(); if (!initialize_started_) Initialize(); scheduler_->Start(); reporting_service_.EnableReporting(); } void UkmService::DisableReporting() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::DisableReporting"; reporting_service_.DisableReporting(); metrics_providers_.OnRecordingDisabled(); scheduler_->Stop(); Flush(); } #if defined(OS_ANDROID) || defined(OS_IOS) void UkmService::OnAppEnterForeground() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::OnAppEnterForeground"; // If initialize_started_ is false, UKM has not yet been started, so bail. The // scheduler will instead be started via EnableReporting(). if (!initialize_started_) return; scheduler_->Start(); } void UkmService::OnAppEnterBackground() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::OnAppEnterBackground"; if (!initialize_started_) return; scheduler_->Stop(); // Give providers a chance to persist ukm data as part of being backgrounded. metrics_providers_.OnAppEnterBackground(); Flush(); } #endif void UkmService::Flush() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (initialize_complete_) BuildAndStoreLog(); reporting_service_.ukm_log_store()->PersistUnsentLogs(); } void UkmService::Purge() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::Purge"; reporting_service_.ukm_log_store()->Purge(); UkmRecorderImpl::Purge(); } // TODO(bmcquade): rename this to something more generic, like // ResetClientState. Consider resetting all prefs here. void UkmService::ResetClientId() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); client_id_ = GenerateClientId(pref_service_); session_id_ = LoadSessionId(pref_service_); report_count_ = 0; } void UkmService::RegisterMetricsProvider( std::unique_ptr provider) { metrics_providers_.RegisterMetricsProvider(std::move(provider)); } // static void UkmService::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterInt64Pref(prefs::kUkmClientId, 0); registry->RegisterIntegerPref(prefs::kUkmSessionId, 0); UkmReportingService::RegisterPrefs(registry); } void UkmService::StartInitTask() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::StartInitTask"; metrics_providers_.AsyncInit(base::Bind(&UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr())); } void UkmService::FinishedInitTask() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::FinishedInitTask"; initialize_complete_ = true; scheduler_->InitTaskComplete(); } void UkmService::RotateLog() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::RotateLog"; if (!reporting_service_.ukm_log_store()->has_unsent_logs()) BuildAndStoreLog(); reporting_service_.Start(); scheduler_->RotationFinished(); } void UkmService::BuildAndStoreLog() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DVLOG(1) << "UkmService::BuildAndStoreLog"; // Suppress generating a log if we have no new data to include. // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. if (sources().empty() && entries().empty()) return; Report report; report.set_client_id(client_id_); report.set_session_id(session_id_); report.set_report_id(++report_count_); StoreRecordingsInReport(&report); metrics::MetricsLog::RecordCoreSystemProfile(client_, report.mutable_system_profile()); metrics_providers_.ProvideSystemProfileMetrics( report.mutable_system_profile()); std::string serialized_log; report.SerializeToString(&serialized_log); reporting_service_.ukm_log_store()->StoreLog(serialized_log); } bool UkmService::ShouldRestrictToWhitelistedEntries() const { return restrict_to_whitelist_entries_; } } // namespace ukm