| // Copyright 2018 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 "chrome/browser/data_use_measurement/chrome_data_use_measurement.h" |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/task/task_traits.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "components/data_use_measurement/core/data_use_ascriber.h" |
| #include "components/data_use_measurement/core/url_request_classifier.h" |
| #include "components/metrics/data_use_tracker.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "services/network/public/cpp/features.h" |
| |
| using content::BrowserThread; |
| |
| namespace data_use_measurement { |
| |
| namespace { |
| // Global instance to be used when network service is enabled, this will never |
| // be deleted. When network service is disabled, this should always be null. |
| ChromeDataUseMeasurement* g_chrome_data_use_measurement = nullptr; |
| |
| DataUseUserData::DataUseContentType GetContentType(const std::string& mime_type, |
| bool is_main_frame_resource, |
| bool is_app_background, |
| bool is_tab_visible) { |
| if (mime_type == "text/html") |
| return is_main_frame_resource ? DataUseUserData::MAIN_FRAME_HTML |
| : DataUseUserData::NON_MAIN_FRAME_HTML; |
| if (mime_type == "text/css") |
| return DataUseUserData::CSS; |
| if (base::StartsWith(mime_type, "image/", base::CompareCase::SENSITIVE)) |
| return DataUseUserData::IMAGE; |
| if (base::EndsWith(mime_type, "javascript", base::CompareCase::SENSITIVE) || |
| base::EndsWith(mime_type, "ecmascript", base::CompareCase::SENSITIVE)) { |
| return DataUseUserData::JAVASCRIPT; |
| } |
| if (mime_type.find("font") != std::string::npos) |
| return DataUseUserData::FONT; |
| if (base::StartsWith(mime_type, "audio/", base::CompareCase::SENSITIVE)) |
| return is_app_background |
| ? DataUseUserData::AUDIO_APPBACKGROUND |
| : (!is_tab_visible ? DataUseUserData::AUDIO_TABBACKGROUND |
| : DataUseUserData::AUDIO); |
| if (base::StartsWith(mime_type, "video/", base::CompareCase::SENSITIVE)) |
| return is_app_background |
| ? DataUseUserData::VIDEO_APPBACKGROUND |
| : (!is_tab_visible ? DataUseUserData::VIDEO_TABBACKGROUND |
| : DataUseUserData::VIDEO); |
| return DataUseUserData::OTHER; |
| } |
| |
| } // namespace |
| |
| // static |
| void ChromeDataUseMeasurement::CreateInstance(PrefService* local_state) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI)); |
| |
| DCHECK(!g_chrome_data_use_measurement); |
| |
| // Do not create when NetworkService is disabled, since data use of URLLoader |
| // is reported via the network delegate callbacks. |
| if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) |
| return; |
| |
| g_chrome_data_use_measurement = new ChromeDataUseMeasurement( |
| nullptr, nullptr, content::GetNetworkConnectionTracker(), local_state); |
| } |
| |
| // static |
| ChromeDataUseMeasurement* ChromeDataUseMeasurement::GetInstance() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || |
| !BrowserThread::IsThreadInitialized(BrowserThread::UI)); |
| |
| return g_chrome_data_use_measurement; |
| } |
| |
| ChromeDataUseMeasurement::ChromeDataUseMeasurement( |
| std::unique_ptr<URLRequestClassifier> url_request_classifier, |
| DataUseAscriber* ascriber, |
| network::NetworkConnectionTracker* network_connection_tracker, |
| PrefService* local_state) |
| : DataUseMeasurement(std::move(url_request_classifier), |
| ascriber, |
| network_connection_tracker), |
| local_state_(local_state) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void ChromeDataUseMeasurement::UpdateDataUseToMetricsService( |
| int64_t total_bytes, |
| bool is_cellular, |
| bool is_metrics_service_usage) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Update data use of user traffic and services distinguishing cellular and |
| // metrics services data use. |
| UpdateMetricsUsagePrefsOnUIThread(total_bytes, is_cellular, |
| is_metrics_service_usage); |
| } |
| |
| void ChromeDataUseMeasurement::ReportNetworkServiceDataUse( |
| int32_t network_traffic_annotation_id_hash, |
| int64_t recv_bytes, |
| int64_t sent_bytes) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); |
| // Negative byte numbres is not a critical problem (i.e. should have no security implications) but |
| // is not expected. TODO(rajendrant): remove these DCHECKs or consider using uint in Mojo instead. |
| DCHECK_GE(recv_bytes, 0); |
| DCHECK_GE(sent_bytes, 0); |
| |
| bool is_user_request = |
| DataUseMeasurement::IsUserRequest(network_traffic_annotation_id_hash); |
| bool is_metrics_service_request = |
| IsMetricsServiceRequest(network_traffic_annotation_id_hash); |
| UpdateMetricsUsagePrefs(recv_bytes, IsCurrentNetworkCellular(), |
| is_metrics_service_request); |
| UpdateMetricsUsagePrefs(sent_bytes, IsCurrentNetworkCellular(), |
| is_metrics_service_request); |
| if (!is_user_request) { |
| ReportDataUsageServices(network_traffic_annotation_id_hash, UPSTREAM, |
| CurrentAppState(), sent_bytes); |
| ReportDataUsageServices(network_traffic_annotation_id_hash, DOWNSTREAM, |
| CurrentAppState(), recv_bytes); |
| } |
| if (!is_user_request || DataUseMeasurement::IsUserDownloadsRequest( |
| network_traffic_annotation_id_hash)) { |
| for (auto& observer : services_data_use_observer_list_) |
| observer.OnServicesDataUse(network_traffic_annotation_id_hash, recv_bytes, |
| sent_bytes); |
| } |
| UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesReceived.Delegate", recv_bytes); |
| UMA_HISTOGRAM_COUNTS_1M("DataUse.BytesSent.Delegate", sent_bytes); |
| #if defined(OS_ANDROID) |
| bytes_transferred_since_last_traffic_stats_query_ += recv_bytes + sent_bytes; |
| MaybeRecordNetworkBytesOS(); |
| #endif |
| } |
| |
| void ChromeDataUseMeasurement::ReportUserTrafficDataUse(bool is_tab_visible, |
| int64_t recv_bytes) { |
| RecordTrafficSizeMetric(true, true, is_tab_visible, recv_bytes); |
| } |
| |
| void ChromeDataUseMeasurement::RecordContentTypeMetric( |
| const std::string& mime_type, |
| bool is_main_frame_resource, |
| bool is_tab_visible, |
| int64_t recv_bytes) { |
| DataUseUserData::DataUseContentType content_type = GetContentType( |
| mime_type, is_main_frame_resource, |
| CurrentAppState() == DataUseUserData::BACKGROUND, is_tab_visible); |
| UMA_HISTOGRAM_SCALED_ENUMERATION("DataUse.ContentType.UserTrafficKB", |
| content_type, recv_bytes, 1024); |
| } |
| |
| void ChromeDataUseMeasurement::UpdateMetricsUsagePrefs( |
| int64_t total_bytes, |
| bool is_cellular, |
| bool is_metrics_service_usage) { |
| PrefService* local_state; |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| local_state = local_state_; |
| } else { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| local_state = g_browser_process->local_state(); |
| } |
| DCHECK(local_state); |
| |
| metrics::DataUseTracker::UpdateMetricsUsagePrefs( |
| base::saturated_cast<int>(total_bytes), is_cellular, |
| is_metrics_service_usage, local_state); |
| } |
| |
| // This function is for forwarding metrics usage pref changes to the metrics |
| // service on the appropriate thread. |
| // TODO(gayane): Reduce the frequency of posting tasks from IO to UI thread. |
| void ChromeDataUseMeasurement::UpdateMetricsUsagePrefsOnUIThread( |
| int64_t total_bytes, |
| bool is_cellular, |
| bool is_metrics_service_usage) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| |
| base::PostTaskWithTraits( |
| FROM_HERE, content::BrowserThread::UI, |
| base::BindOnce(&ChromeDataUseMeasurement::UpdateMetricsUsagePrefs, |
| base::Unretained(this), total_bytes, is_cellular, |
| is_metrics_service_usage)); |
| } |
| |
| } // namespace data_use_measurement |