| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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. | |
| 4 | |
| 5 #include "components/data_use_measurement/content/data_use_measurement.h" | |
| 6 | |
| 7 #include "base/metrics/histogram_macros.h" | |
| 8 #include "base/metrics/sparse_histogram.h" | |
| 9 #include "base/strings/stringprintf.h" | |
| 10 #include "build/build_config.h" | |
| 11 #include "components/data_use_measurement/core/data_use_user_data.h" | |
| 12 #include "content/public/browser/resource_request_info.h" | |
| 13 #include "net/base/network_change_notifier.h" | |
| 14 #include "net/base/upload_data_stream.h" | |
| 15 #include "net/http/http_response_headers.h" | |
| 16 #include "net/url_request/url_request.h" | |
| 17 | |
| 18 #if defined(OS_ANDROID) | |
| 19 #include "net/android/traffic_stats.h" | |
| 20 #endif | |
| 21 | |
| 22 namespace data_use_measurement { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Records the occurrence of |sample| in |name| histogram. Conventional UMA | |
| 27 // histograms are not used because the |name| is not static. | |
| 28 void RecordUMAHistogramCount(const std::string& name, int64_t sample) { | |
| 29 base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet( | |
| 30 name, | |
| 31 1, // Minimum sample size in bytes. | |
| 32 1000000, // Maximum sample size in bytes. Should cover most of the | |
| 33 // requests by services. | |
| 34 50, // Bucket count. | |
| 35 base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 36 histogram_pointer->Add(sample); | |
| 37 } | |
| 38 | |
| 39 // This function increases the value of |sample| bucket in |name| sparse | |
| 40 // histogram by |value|. Conventional UMA histograms are not used because |name| | |
| 41 // is not static. | |
| 42 void IncreaseSparseHistogramByValue(const std::string& name, | |
| 43 int64_t sample, | |
| 44 int64_t value) { | |
| 45 base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( | |
| 46 name, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 47 histogram->AddCount(sample, value); | |
| 48 } | |
| 49 | |
| 50 #if defined(OS_ANDROID) | |
| 51 void IncrementLatencyHistogramByCount(const std::string& name, | |
| 52 const base::TimeDelta& latency, | |
| 53 int64_t count) { | |
| 54 base::HistogramBase* histogram_pointer = base::Histogram::FactoryTimeGet( | |
| 55 name, | |
| 56 base::TimeDelta::FromMilliseconds(1), // Minimum sample | |
| 57 base::TimeDelta::FromHours(1), // Maximum sample | |
| 58 50, // Bucket count. | |
| 59 base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 60 histogram_pointer->AddCount(latency.InMilliseconds(), count); | |
| 61 } | |
| 62 #endif | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 DataUseMeasurement::DataUseMeasurement( | |
| 67 const metrics::UpdateUsagePrefCallbackType& metrics_data_use_forwarder) | |
| 68 : metrics_data_use_forwarder_(metrics_data_use_forwarder) | |
| 69 #if defined(OS_ANDROID) | |
| 70 , | |
| 71 app_state_(base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES), | |
| 72 app_listener_(new base::android::ApplicationStatusListener( | |
| 73 base::Bind(&DataUseMeasurement::OnApplicationStateChange, | |
| 74 base::Unretained(this)))), | |
| 75 rx_bytes_os_(0), | |
| 76 tx_bytes_os_(0), | |
| 77 bytes_transferred_since_last_traffic_stats_query_(0), | |
| 78 no_reads_since_background_(false) | |
| 79 #endif | |
| 80 { | |
| 81 } | |
| 82 | |
| 83 DataUseMeasurement::~DataUseMeasurement(){}; | |
| 84 | |
| 85 void DataUseMeasurement::OnBeforeURLRequest(net::URLRequest* request) { | |
| 86 DataUseUserData* data_use_user_data = reinterpret_cast<DataUseUserData*>( | |
| 87 request->GetUserData(DataUseUserData::kUserDataKey)); | |
| 88 if (!data_use_user_data) { | |
| 89 data_use_user_data = new DataUseUserData( | |
| 90 DataUseUserData::ServiceName::NOT_TAGGED, CurrentAppState()); | |
| 91 request->SetUserData(DataUseUserData::kUserDataKey, data_use_user_data); | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 void DataUseMeasurement::OnBeforeRedirect(const net::URLRequest& request, | |
| 96 const GURL& new_location) { | |
| 97 // Recording data use of request on redirects. | |
| 98 // TODO(rajendrant): May not be needed when http://crbug/651957 is fixed. | |
| 99 UpdateDataUsePrefs(request); | |
| 100 } | |
| 101 | |
| 102 void DataUseMeasurement::OnNetworkBytesReceived(const net::URLRequest& request, | |
| 103 int64_t bytes_received) { | |
| 104 UMA_HISTOGRAM_COUNTS("DataUse.BytesReceived.Delegate", bytes_received); | |
| 105 ReportDataUseUMA(request, DOWNSTREAM, bytes_received); | |
| 106 #if defined(OS_ANDROID) | |
| 107 bytes_transferred_since_last_traffic_stats_query_ += bytes_received; | |
| 108 #endif | |
| 109 } | |
| 110 | |
| 111 void DataUseMeasurement::OnNetworkBytesSent(const net::URLRequest& request, | |
| 112 int64_t bytes_sent) { | |
| 113 UMA_HISTOGRAM_COUNTS("DataUse.BytesSent.Delegate", bytes_sent); | |
| 114 ReportDataUseUMA(request, UPSTREAM, bytes_sent); | |
| 115 #if defined(OS_ANDROID) | |
| 116 bytes_transferred_since_last_traffic_stats_query_ += bytes_sent; | |
| 117 #endif | |
| 118 } | |
| 119 | |
| 120 void DataUseMeasurement::OnCompleted(const net::URLRequest& request, | |
| 121 bool started) { | |
| 122 // TODO(amohammadkhan): Verify that there is no double recording in data use | |
| 123 // of redirected requests. | |
| 124 UpdateDataUsePrefs(request); | |
| 125 #if defined(OS_ANDROID) | |
| 126 MaybeRecordNetworkBytesOS(); | |
| 127 #endif | |
| 128 } | |
| 129 | |
| 130 void DataUseMeasurement::ReportDataUseUMA(const net::URLRequest& request, | |
| 131 TrafficDirection dir, | |
| 132 int64_t bytes) { | |
| 133 bool is_user_traffic = IsUserInitiatedRequest(request); | |
| 134 bool is_connection_cellular = | |
| 135 net::NetworkChangeNotifier::IsConnectionCellular( | |
| 136 net::NetworkChangeNotifier::GetConnectionType()); | |
| 137 | |
| 138 DataUseUserData* attached_service_data = static_cast<DataUseUserData*>( | |
| 139 request.GetUserData(DataUseUserData::kUserDataKey)); | |
| 140 DataUseUserData::ServiceName service_name = DataUseUserData::NOT_TAGGED; | |
| 141 DataUseUserData::AppState old_app_state = DataUseUserData::FOREGROUND; | |
| 142 DataUseUserData::AppState new_app_state = DataUseUserData::UNKNOWN; | |
| 143 | |
| 144 if (attached_service_data) { | |
| 145 service_name = attached_service_data->service_name(); | |
| 146 old_app_state = attached_service_data->app_state(); | |
| 147 } | |
| 148 if (old_app_state == CurrentAppState()) | |
| 149 new_app_state = old_app_state; | |
| 150 | |
| 151 if (attached_service_data && old_app_state != new_app_state) | |
| 152 attached_service_data->set_app_state(CurrentAppState()); | |
| 153 | |
| 154 RecordUMAHistogramCount( | |
| 155 GetHistogramName(is_user_traffic ? "DataUse.TrafficSize.User" | |
| 156 : "DataUse.TrafficSize.System", | |
| 157 dir, new_app_state, is_connection_cellular), | |
| 158 bytes); | |
| 159 | |
| 160 if (!is_user_traffic) { | |
| 161 ReportDataUsageServices(service_name, dir, new_app_state, | |
| 162 is_connection_cellular, bytes); | |
| 163 } | |
| 164 #if defined(OS_ANDROID) | |
| 165 if (dir == DOWNSTREAM && CurrentAppState() == DataUseUserData::BACKGROUND) { | |
| 166 DCHECK(!last_app_background_time_.is_null()); | |
| 167 | |
| 168 const base::TimeDelta time_since_background = | |
| 169 base::TimeTicks::Now() - last_app_background_time_; | |
| 170 IncrementLatencyHistogramByCount( | |
| 171 is_user_traffic ? "DataUse.BackgroundToDataRecievedPerByte.User" | |
| 172 : "DataUse.BackgroundToDataRecievedPerByte.System", | |
| 173 time_since_background, bytes); | |
| 174 if (no_reads_since_background_) { | |
| 175 no_reads_since_background_ = false; | |
| 176 IncrementLatencyHistogramByCount( | |
| 177 is_user_traffic ? "DataUse.BackgroundToFirstDownstream.User" | |
| 178 : "DataUse.BackgroundToFirstDownstream.System", | |
| 179 time_since_background, 1); | |
| 180 } | |
| 181 } | |
| 182 #endif | |
| 183 } | |
| 184 | |
| 185 void DataUseMeasurement::UpdateDataUsePrefs( | |
| 186 const net::URLRequest& request) const { | |
| 187 bool is_connection_cellular = | |
| 188 net::NetworkChangeNotifier::IsConnectionCellular( | |
| 189 net::NetworkChangeNotifier::GetConnectionType()); | |
| 190 | |
| 191 DataUseUserData* attached_service_data = static_cast<DataUseUserData*>( | |
| 192 request.GetUserData(DataUseUserData::kUserDataKey)); | |
| 193 DataUseUserData::ServiceName service_name = | |
| 194 attached_service_data ? attached_service_data->service_name() | |
| 195 : DataUseUserData::NOT_TAGGED; | |
| 196 | |
| 197 // Update data use prefs for cellular connections. | |
| 198 if (!metrics_data_use_forwarder_.is_null()) { | |
| 199 metrics_data_use_forwarder_.Run( | |
| 200 DataUseUserData::GetServiceNameAsString(service_name), | |
| 201 request.GetTotalSentBytes() + request.GetTotalReceivedBytes(), | |
| 202 is_connection_cellular); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 // static | |
| 207 bool DataUseMeasurement::IsUserInitiatedRequest( | |
| 208 const net::URLRequest& request) { | |
| 209 // Having ResourceRequestInfo in the URL request is a sign that the request is | |
| 210 // for a web content from user. For now we could add a condition to check | |
| 211 // ProcessType in info is content::PROCESS_TYPE_RENDERER, but it won't be | |
| 212 // compatible with upcoming PlzNavigate architecture. So just existence of | |
| 213 // ResourceRequestInfo is verified, and the current check should be compatible | |
| 214 // with upcoming changes in PlzNavigate. | |
| 215 // TODO(rajendrant): Verify this condition for different use cases. See | |
| 216 // crbug.com/626063. | |
| 217 return content::ResourceRequestInfo::ForRequest(&request) != nullptr; | |
| 218 } | |
| 219 | |
| 220 #if defined(OS_ANDROID) | |
| 221 void DataUseMeasurement::OnApplicationStateChangeForTesting( | |
| 222 base::android::ApplicationState application_state) { | |
| 223 OnApplicationStateChange(application_state); | |
| 224 } | |
| 225 #endif | |
| 226 | |
| 227 DataUseUserData::AppState DataUseMeasurement::CurrentAppState() const { | |
| 228 #if defined(OS_ANDROID) | |
| 229 if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) | |
| 230 return DataUseUserData::BACKGROUND; | |
| 231 #endif | |
| 232 // If the OS is not Android, all the requests are considered Foreground. | |
| 233 return DataUseUserData::FOREGROUND; | |
| 234 } | |
| 235 | |
| 236 std::string DataUseMeasurement::GetHistogramName( | |
| 237 const char* prefix, | |
| 238 TrafficDirection dir, | |
| 239 DataUseUserData::AppState app_state, | |
| 240 bool is_connection_cellular) const { | |
| 241 return base::StringPrintf( | |
| 242 "%s.%s.%s.%s", prefix, dir == UPSTREAM ? "Upstream" : "Downstream", | |
| 243 app_state == DataUseUserData::UNKNOWN | |
| 244 ? "Unknown" | |
| 245 : (app_state == DataUseUserData::FOREGROUND ? "Foreground" | |
| 246 : "Background"), | |
| 247 is_connection_cellular ? "Cellular" : "NotCellular"); | |
| 248 } | |
| 249 | |
| 250 #if defined(OS_ANDROID) | |
| 251 void DataUseMeasurement::OnApplicationStateChange( | |
| 252 base::android::ApplicationState application_state) { | |
| 253 app_state_ = application_state; | |
| 254 if (app_state_ != base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { | |
| 255 last_app_background_time_ = base::TimeTicks::Now(); | |
| 256 no_reads_since_background_ = true; | |
| 257 MaybeRecordNetworkBytesOS(); | |
| 258 } else { | |
| 259 last_app_background_time_ = base::TimeTicks(); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 void DataUseMeasurement::MaybeRecordNetworkBytesOS() { | |
| 264 // Minimum number of bytes that should be reported by the network delegate | |
| 265 // before Android's TrafficStats API is queried (if Chrome is not in | |
| 266 // background). This reduces the overhead of repeatedly calling the API. | |
| 267 static const int64_t kMinDelegateBytes = 25000; | |
| 268 | |
| 269 if (bytes_transferred_since_last_traffic_stats_query_ < kMinDelegateBytes && | |
| 270 CurrentAppState() == DataUseUserData::FOREGROUND) { | |
| 271 return; | |
| 272 } | |
| 273 bytes_transferred_since_last_traffic_stats_query_ = 0; | |
| 274 int64_t bytes = 0; | |
| 275 // Query Android traffic stats directly instead of registering with the | |
| 276 // DataUseAggregator since the latter does not provide notifications for | |
| 277 // the incognito traffic. | |
| 278 if (net::android::traffic_stats::GetCurrentUidRxBytes(&bytes)) { | |
| 279 if (rx_bytes_os_ != 0) { | |
| 280 DCHECK_GE(bytes, rx_bytes_os_); | |
| 281 UMA_HISTOGRAM_COUNTS("DataUse.BytesReceived.OS", bytes - rx_bytes_os_); | |
| 282 } | |
| 283 rx_bytes_os_ = bytes; | |
| 284 } | |
| 285 | |
| 286 if (net::android::traffic_stats::GetCurrentUidTxBytes(&bytes)) { | |
| 287 if (tx_bytes_os_ != 0) { | |
| 288 DCHECK_GE(bytes, tx_bytes_os_); | |
| 289 UMA_HISTOGRAM_COUNTS("DataUse.BytesSent.OS", bytes - tx_bytes_os_); | |
| 290 } | |
| 291 tx_bytes_os_ = bytes; | |
| 292 } | |
| 293 } | |
| 294 #endif | |
| 295 | |
| 296 void DataUseMeasurement::ReportDataUsageServices( | |
| 297 DataUseUserData::ServiceName service, | |
| 298 TrafficDirection dir, | |
| 299 DataUseUserData::AppState app_state, | |
| 300 bool is_connection_cellular, | |
| 301 int64_t message_size) const { | |
| 302 RecordUMAHistogramCount( | |
| 303 "DataUse.MessageSize." + DataUseUserData::GetServiceNameAsString(service), | |
| 304 message_size); | |
| 305 if (message_size > 0) { | |
| 306 IncreaseSparseHistogramByValue( | |
| 307 GetHistogramName("DataUse.MessageSize.AllServices", dir, app_state, | |
| 308 is_connection_cellular), | |
| 309 service, message_size); | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 } // namespace data_use_measurement | |
| OLD | NEW |