| // Copyright 2015 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 "ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h" |
| |
| #include <stdint.h> |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/process/process_metrics.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string16.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "components/browser_sync/profile_sync_service.h" |
| #include "components/crash/core/common/crash_keys.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/keyed_service/core/service_access_type.h" |
| #include "components/metrics/call_stack_profile_metrics_provider.h" |
| #include "components/metrics/drive_metrics_provider.h" |
| #include "components/metrics/metrics_log_uploader.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/metrics/metrics_reporting_default_state.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/metrics/net/cellular_logic_helper.h" |
| #include "components/metrics/net/net_metrics_log_uploader.h" |
| #include "components/metrics/net/network_metrics_provider.h" |
| #include "components/metrics/profiler/profiler_metrics_provider.h" |
| #include "components/metrics/profiler/tracking_synchronizer.h" |
| #include "components/metrics/stability_metrics_helper.h" |
| #include "components/metrics/ui/screen_info_metrics_provider.h" |
| #include "components/metrics/url_constants.h" |
| #include "components/metrics/version_utils.h" |
| #include "components/omnibox/browser/omnibox_metrics_provider.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/core/browser/signin_status_metrics_provider.h" |
| #include "components/sync/device_info/device_count_metrics_provider.h" |
| #include "components/ukm/ukm_service.h" |
| #include "components/variations/variations_associated_data.h" |
| #include "components/version_info/version_info.h" |
| #include "ios/chrome/browser/application_context.h" |
| #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h" |
| #include "ios/chrome/browser/chrome_paths.h" |
| #include "ios/chrome/browser/google/google_brand.h" |
| #include "ios/chrome/browser/history/history_service_factory.h" |
| #include "ios/chrome/browser/metrics/ios_chrome_stability_metrics_provider.h" |
| #include "ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h" |
| #include "ios/chrome/browser/signin/ios_chrome_signin_status_metrics_provider_delegate.h" |
| #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
| #include "ios/chrome/browser/sync/ios_chrome_sync_client.h" |
| #include "ios/chrome/browser/tab_parenting_global_observer.h" |
| #include "ios/chrome/browser/tabs/tab_model_list.h" |
| #include "ios/chrome/browser/translate/translate_ranker_metrics_provider.h" |
| #include "ios/chrome/common/channel_info.h" |
| #include "ios/web/public/web_thread.h" |
| |
| IOSChromeMetricsServiceClient::IOSChromeMetricsServiceClient( |
| metrics::MetricsStateManager* state_manager) |
| : metrics_state_manager_(state_manager), |
| stability_metrics_provider_(nullptr), |
| profiler_metrics_provider_(nullptr), |
| drive_metrics_provider_(nullptr), |
| start_time_(base::TimeTicks::Now()), |
| has_uploaded_profiler_data_(false), |
| weak_ptr_factory_(this) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| RegisterForNotifications(); |
| } |
| |
| IOSChromeMetricsServiceClient::~IOSChromeMetricsServiceClient() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| } |
| |
| // static |
| std::unique_ptr<IOSChromeMetricsServiceClient> |
| IOSChromeMetricsServiceClient::Create( |
| metrics::MetricsStateManager* state_manager) { |
| // Perform two-phase initialization so that |client->metrics_service_| only |
| // receives pointers to fully constructed objects. |
| std::unique_ptr<IOSChromeMetricsServiceClient> client( |
| new IOSChromeMetricsServiceClient(state_manager)); |
| client->Initialize(); |
| |
| return client; |
| } |
| |
| // static |
| void IOSChromeMetricsServiceClient::RegisterPrefs( |
| PrefRegistrySimple* registry) { |
| metrics::MetricsService::RegisterPrefs(registry); |
| metrics::StabilityMetricsHelper::RegisterPrefs(registry); |
| metrics::RegisterMetricsReportingStatePrefs(registry); |
| ukm::UkmService::RegisterPrefs(registry); |
| } |
| |
| metrics::MetricsService* IOSChromeMetricsServiceClient::GetMetricsService() { |
| return metrics_service_.get(); |
| } |
| |
| ukm::UkmService* IOSChromeMetricsServiceClient::GetUkmService() { |
| return ukm_service_.get(); |
| } |
| |
| void IOSChromeMetricsServiceClient::SetMetricsClientId( |
| const std::string& client_id) { |
| crash_keys::SetMetricsClientIdFromGUID(client_id); |
| } |
| |
| int32_t IOSChromeMetricsServiceClient::GetProduct() { |
| return metrics::ChromeUserMetricsExtension::CHROME; |
| } |
| |
| std::string IOSChromeMetricsServiceClient::GetApplicationLocale() { |
| return GetApplicationContext()->GetApplicationLocale(); |
| } |
| |
| bool IOSChromeMetricsServiceClient::GetBrand(std::string* brand_code) { |
| return ios::google_brand::GetBrand(brand_code); |
| } |
| |
| metrics::SystemProfileProto::Channel |
| IOSChromeMetricsServiceClient::GetChannel() { |
| return metrics::AsProtobufChannel(::GetChannel()); |
| } |
| |
| std::string IOSChromeMetricsServiceClient::GetVersionString() { |
| return metrics::GetVersionString(); |
| } |
| |
| void IOSChromeMetricsServiceClient::InitializeSystemProfileMetrics( |
| const base::Closure& done_callback) { |
| finished_init_task_callback_ = done_callback; |
| drive_metrics_provider_->GetDriveMetrics( |
| base::Bind(&IOSChromeMetricsServiceClient::OnInitTaskGotDriveMetrics, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void IOSChromeMetricsServiceClient::CollectFinalMetricsForLog( |
| const base::Closure& done_callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| collect_final_metrics_done_callback_ = done_callback; |
| |
| if (ShouldIncludeProfilerDataInLog()) { |
| // Fetch profiler data. This will call into |
| // |FinishedReceivingProfilerData()| when the task completes. |
| metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously( |
| weak_ptr_factory_.GetWeakPtr()); |
| } else { |
| CollectFinalHistograms(); |
| } |
| } |
| |
| std::unique_ptr<metrics::MetricsLogUploader> |
| IOSChromeMetricsServiceClient::CreateUploader( |
| base::StringPiece server_url, |
| base::StringPiece mime_type, |
| metrics::MetricsLogUploader::MetricServiceType service_type, |
| const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) { |
| return base::MakeUnique<metrics::NetMetricsLogUploader>( |
| GetApplicationContext()->GetSystemURLRequestContext(), server_url, |
| mime_type, service_type, on_upload_complete); |
| } |
| |
| base::TimeDelta IOSChromeMetricsServiceClient::GetStandardUploadInterval() { |
| return metrics::GetUploadInterval(); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnRendererProcessCrash() { |
| stability_metrics_provider_->LogRendererCrash(); |
| } |
| |
| void IOSChromeMetricsServiceClient::WebStateDidStartLoading( |
| web::WebState* web_state) { |
| metrics_service_->OnApplicationNotIdle(); |
| } |
| |
| void IOSChromeMetricsServiceClient::WebStateDidStopLoading( |
| web::WebState* web_state) { |
| metrics_service_->OnApplicationNotIdle(); |
| } |
| |
| void IOSChromeMetricsServiceClient::Initialize() { |
| PrefService* local_state = GetApplicationContext()->GetLocalState(); |
| metrics_service_ = base::MakeUnique<metrics::MetricsService>( |
| metrics_state_manager_, this, local_state); |
| |
| if (base::FeatureList::IsEnabled(ukm::kUkmFeature)) |
| ukm_service_ = base::MakeUnique<ukm::UkmService>(local_state, this); |
| |
| // Register metrics providers. |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<metrics::NetworkMetricsProvider>()); |
| |
| // Currently, we configure OmniboxMetricsProvider to not log events to UMA |
| // if there is a single incognito session visible. In the future, it may |
| // be worth revisiting this to still log events from non-incognito sessions. |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<OmniboxMetricsProvider>( |
| base::Bind(&::IsOffTheRecordSessionActive))); |
| |
| { |
| auto stability_metrics_provider = |
| base::MakeUnique<IOSChromeStabilityMetricsProvider>( |
| GetApplicationContext()->GetLocalState()); |
| stability_metrics_provider_ = stability_metrics_provider.get(); |
| metrics_service_->RegisterMetricsProvider( |
| std::move(stability_metrics_provider)); |
| } |
| |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<metrics::ScreenInfoMetricsProvider>()); |
| |
| { |
| auto drive_metrics_provider = |
| base::MakeUnique<metrics::DriveMetricsProvider>(ios::FILE_LOCAL_STATE); |
| drive_metrics_provider_ = drive_metrics_provider.get(); |
| metrics_service_->RegisterMetricsProvider( |
| std::move(drive_metrics_provider)); |
| } |
| |
| { |
| auto profiler_metrics_provider = |
| base::MakeUnique<metrics::ProfilerMetricsProvider>( |
| base::Bind(&metrics::IsCellularLogicEnabled)); |
| profiler_metrics_provider_ = profiler_metrics_provider.get(); |
| metrics_service_->RegisterMetricsProvider( |
| std::move(profiler_metrics_provider)); |
| } |
| |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<metrics::CallStackProfileMetricsProvider>()); |
| |
| metrics_service_->RegisterMetricsProvider( |
| SigninStatusMetricsProvider::CreateInstance( |
| base::MakeUnique<IOSChromeSigninStatusMetricsProviderDelegate>())); |
| |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<MobileSessionShutdownMetricsProvider>( |
| metrics_service_.get())); |
| |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<syncer::DeviceCountMetricsProvider>( |
| base::Bind(&IOSChromeSyncClient::GetDeviceInfoTrackers))); |
| |
| metrics_service_->RegisterMetricsProvider( |
| base::MakeUnique<translate::TranslateRankerMetricsProvider>()); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnInitTaskGotDriveMetrics() { |
| finished_init_task_callback_.Run(); |
| } |
| |
| bool IOSChromeMetricsServiceClient::ShouldIncludeProfilerDataInLog() { |
| // Upload profiler data at most once per session. |
| if (has_uploaded_profiler_data_) |
| return false; |
| |
| // For each log, flip a fair coin. Thus, profiler data is sent with the first |
| // log with probability 50%, with the second log with probability 25%, and so |
| // on. As a result, uploaded data is biased toward earlier logs. |
| // TODO(isherman): Explore other possible algorithms, and choose one that |
| // might be more appropriate. For example, it might be reasonable to include |
| // profiler data with some fixed probability, so that a given client might |
| // upload profiler data more than once; but on average, clients won't upload |
| // too much data. |
| if (base::RandDouble() < 0.5) |
| return false; |
| |
| has_uploaded_profiler_data_ = true; |
| return true; |
| } |
| |
| void IOSChromeMetricsServiceClient::ReceivedProfilerData( |
| const metrics::ProfilerDataAttributes& attributes, |
| const tracked_objects::ProcessDataPhaseSnapshot& process_data_phase, |
| const metrics::ProfilerEvents& past_events) { |
| profiler_metrics_provider_->RecordProfilerData( |
| process_data_phase, attributes.process_id, attributes.process_type, |
| attributes.profiling_phase, attributes.phase_start - start_time_, |
| attributes.phase_finish - start_time_, past_events); |
| } |
| |
| void IOSChromeMetricsServiceClient::FinishedReceivingProfilerData() { |
| CollectFinalHistograms(); |
| } |
| |
| void IOSChromeMetricsServiceClient::CollectFinalHistograms() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| // TODO(ios): Try to extract the flow below into a utility function that is |
| // shared between the iOS port's usage and |
| // ChromeMetricsServiceClient::CollectFinalHistograms()'s usage of |
| // MetricsMemoryDetails. |
| std::unique_ptr<base::ProcessMetrics> process_metrics( |
| base::ProcessMetrics::CreateProcessMetrics( |
| base::GetCurrentProcessHandle())); |
| UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", |
| process_metrics->GetWorkingSetSize() / 1024); |
| collect_final_metrics_done_callback_.Run(); |
| } |
| |
| void IOSChromeMetricsServiceClient::RegisterForNotifications() { |
| tab_parented_subscription_ = |
| TabParentingGlobalObserver::GetInstance()->RegisterCallback( |
| base::Bind(&IOSChromeMetricsServiceClient::OnTabParented, |
| base::Unretained(this))); |
| omnibox_url_opened_subscription_ = |
| OmniboxEventGlobalTracker::GetInstance()->RegisterCallback( |
| base::Bind(&IOSChromeMetricsServiceClient::OnURLOpenedFromOmnibox, |
| base::Unretained(this))); |
| |
| std::vector<ios::ChromeBrowserState*> loaded_browser_states = |
| GetApplicationContext() |
| ->GetChromeBrowserStateManager() |
| ->GetLoadedBrowserStates(); |
| for (ios::ChromeBrowserState* browser_state : loaded_browser_states) { |
| RegisterForBrowserStateEvents(browser_state); |
| } |
| } |
| |
| void IOSChromeMetricsServiceClient::RegisterForBrowserStateEvents( |
| ios::ChromeBrowserState* browser_state) { |
| history::HistoryService* history_service = |
| ios::HistoryServiceFactory::GetForBrowserState( |
| browser_state, ServiceAccessType::IMPLICIT_ACCESS); |
| ObserveServiceForDeletions(history_service); |
| browser_sync::ProfileSyncService* sync = |
| IOSChromeProfileSyncServiceFactory::GetInstance()->GetForBrowserState( |
| browser_state); |
| ObserveServiceForSyncDisables(static_cast<syncer::SyncService*>(sync)); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnTabParented(web::WebState* web_state) { |
| metrics_service_->OnApplicationNotIdle(); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnURLOpenedFromOmnibox(OmniboxLog* log) { |
| metrics_service_->OnApplicationNotIdle(); |
| } |
| |
| metrics::EnableMetricsDefault |
| IOSChromeMetricsServiceClient::GetMetricsReportingDefaultState() { |
| return metrics::GetMetricsReportingDefaultState( |
| GetApplicationContext()->GetLocalState()); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnHistoryDeleted() { |
| if (ukm_service_) |
| ukm_service_->Purge(); |
| } |
| |
| void IOSChromeMetricsServiceClient::OnSyncPrefsChanged(bool must_purge) { |
| if (!ukm_service_) |
| return; |
| if (must_purge) { |
| ukm_service_->Purge(); |
| ukm_service_->ResetClientId(); |
| } |
| // Signal service manager to enable/disable UKM based on new state. |
| UpdateRunningServices(); |
| } |
| |
| bool IOSChromeMetricsServiceClient::IsHistorySyncEnabledOnAllProfiles() { |
| return SyncDisableObserver::IsHistorySyncEnabledOnAllProfiles(); |
| } |