1 // Copyright 2013 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.
5 #include "chrome/browser/net/chrome_network_data_saving_metrics.h"
7 #include "base/metrics/histogram.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "chrome/common/pref_names.h"
13 #if defined(OS_ANDROID) || defined(OS_IOS)
16 // The number of days of history stored in the content lengths prefs.
17 const size_t kNumDaysInHistory
= 60;
19 // Increments an int64, stored as a string, in a ListPref at the specified
20 // index. The value must already exist and be a string representation of a
22 void AddInt64ToListPref(size_t index
,
24 base::ListValue
* list_update
) {
26 std::string old_string_value
;
27 bool rv
= list_update
->GetString(index
, &old_string_value
);
30 rv
= base::StringToInt64(old_string_value
, &value
);
34 list_update
->Set(index
, Value::CreateStringValue(base::Int64ToString(value
)));
37 int64
ListPrefInt64Value(const base::ListValue
& list_update
, size_t index
) {
38 std::string string_value
;
39 if (!list_update
.GetString(index
, &string_value
)) {
45 bool rv
= base::StringToInt64(string_value
, &value
);
50 // Report UMA metrics for daily data reductions.
51 void RecordDailyContentLengthHistograms(
52 int64 original_length
,
53 int64 received_length
,
54 int64 original_length_with_data_reduction_enabled
,
55 int64 received_length_with_data_reduction_enabled
,
56 int64 original_length_via_data_reduction_proxy
,
57 int64 received_length_via_data_reduction_proxy
) {
58 // Report daily UMA only for days having received content.
59 if (original_length
<= 0 || received_length
<= 0)
62 // Record metrics in KB.
64 "Net.DailyOriginalContentLength", original_length
>> 10);
66 "Net.DailyContentLength", received_length
>> 10);
68 // UMA percentage cannot be negative.
69 if (original_length
> received_length
) {
70 percent
= (100 * (original_length
- received_length
)) / original_length
;
72 UMA_HISTOGRAM_PERCENTAGE("Net.DailyContentSavingPercent", percent
);
74 if (original_length_with_data_reduction_enabled
<= 0 ||
75 received_length_with_data_reduction_enabled
<= 0) {
80 "Net.DailyOriginalContentLength_DataReductionProxyEnabled",
81 original_length_with_data_reduction_enabled
>> 10);
83 "Net.DailyContentLength_DataReductionProxyEnabled",
84 received_length_with_data_reduction_enabled
>> 10);
86 int percent_data_reduction_proxy_enabled
= 0;
87 // UMA percentage cannot be negative.
88 if (original_length_with_data_reduction_enabled
>
89 received_length_with_data_reduction_enabled
) {
90 percent_data_reduction_proxy_enabled
=
91 100 * (original_length_with_data_reduction_enabled
-
92 received_length_with_data_reduction_enabled
) /
93 original_length_with_data_reduction_enabled
;
95 UMA_HISTOGRAM_PERCENTAGE(
96 "Net.DailyContentSavingPercent_DataReductionProxyEnabled",
97 percent_data_reduction_proxy_enabled
);
99 UMA_HISTOGRAM_PERCENTAGE(
100 "Net.DailyContentPercent_DataReductionProxyEnabled",
101 (100 * received_length_with_data_reduction_enabled
) / received_length
);
103 if (original_length_via_data_reduction_proxy
<= 0 ||
104 received_length_via_data_reduction_proxy
<= 0) {
108 UMA_HISTOGRAM_COUNTS(
109 "Net.DailyOriginalContentLength_ViaDataReductionProxy",
110 original_length_via_data_reduction_proxy
>> 10);
111 UMA_HISTOGRAM_COUNTS(
112 "Net.DailyContentLength_ViaDataReductionProxy",
113 received_length_via_data_reduction_proxy
>> 10);
114 int percent_via_data_reduction_proxy
= 0;
115 if (original_length_via_data_reduction_proxy
>
116 received_length_via_data_reduction_proxy
) {
117 percent_via_data_reduction_proxy
=
118 100 * (original_length_via_data_reduction_proxy
-
119 received_length_via_data_reduction_proxy
) /
120 original_length_via_data_reduction_proxy
;
122 UMA_HISTOGRAM_PERCENTAGE(
123 "Net.DailyContentSavingPercent_ViaDataReductionProxy",
124 percent_via_data_reduction_proxy
);
125 UMA_HISTOGRAM_PERCENTAGE(
126 "Net.DailyContentPercent_ViaDataReductionProxy",
127 (100 * received_length_via_data_reduction_proxy
) / received_length
);
130 // Ensure list has exactly |length| elements, either by truncating at the
131 // front, or appending "0"'s to the back.
132 void MaintainContentLengthPrefsWindow(base::ListValue
* list
, size_t length
) {
133 // Remove data for old days from the front.
134 while (list
->GetSize() > length
)
135 list
->Remove(0, NULL
);
136 // Newly added lists are empty. Add entries to back to fill the window,
137 // each initialized to zero.
138 while (list
->GetSize() < length
)
139 list
->AppendString(base::Int64ToString(0));
140 DCHECK_EQ(length
, list
->GetSize());
143 // Update list for date change and ensure list has exactly |length| elements.
144 // The last entry in each list will be for the current day after the update.
145 void MaintainContentLengthPrefsForDateChange(
146 base::ListValue
* original_update
,
147 base::ListValue
* received_update
,
148 int days_since_last_update
) {
149 if (days_since_last_update
== -1) {
150 // The system may go backwards in time by up to a day for legitimate
151 // reasons, such as with changes to the time zone. In such cases, we
152 // keep adding to the current day.
153 // Note: we accept the fact that some reported data is shifted to
154 // the adjacent day if users travel back and forth across time zones.
155 days_since_last_update
= 0;
156 } else if (days_since_last_update
< -1) {
157 // Erase all entries if the system went backwards in time by more than
159 original_update
->Clear();
160 received_update
->Clear();
162 days_since_last_update
= kNumDaysInHistory
;
164 DCHECK_GE(days_since_last_update
, 0);
166 // Add entries for days since last update event. This will make the
167 // lists longer than kNumDaysInHistory. The additional items will be cut off
168 // from the head of the lists by |MaintainContentLengthPrefsWindow|, below.
170 i
< days_since_last_update
&& i
< static_cast<int>(kNumDaysInHistory
);
172 original_update
->AppendString(base::Int64ToString(0));
173 received_update
->AppendString(base::Int64ToString(0));
176 // Entries for new days may have been appended. Maintain the invariant that
177 // there should be exactly |kNumDaysInHistory| days in the histories.
178 MaintainContentLengthPrefsWindow(original_update
, kNumDaysInHistory
);
179 MaintainContentLengthPrefsWindow(received_update
, kNumDaysInHistory
);
182 // DailyDataSavingUpdate maintains a pair of data saving prefs, original_update_
183 // and received_update_. pref_original is a list of |kNumDaysInHistory| elements
184 // of daily total original content lengths for the past |kNumDaysInHistory|
185 // days. pref_received is the corresponding list of the daily total received
187 class DailyDataSavingUpdate
{
189 DailyDataSavingUpdate(
190 const char* pref_original
, const char* pref_received
,
191 PrefService
* pref_service
)
192 : pref_original_(pref_original
),
193 pref_received_(pref_received
),
194 original_update_(pref_service
, pref_original_
),
195 received_update_(pref_service
, pref_received_
) {
198 void UpdateForDataChange(int days_since_last_update
) {
199 // New empty lists may have been created. Maintain the invariant that
200 // there should be exactly |kNumDaysInHistory| days in the histories.
201 MaintainContentLengthPrefsWindow(original_update_
.Get(), kNumDaysInHistory
);
202 MaintainContentLengthPrefsWindow(received_update_
.Get(), kNumDaysInHistory
);
203 if (days_since_last_update
) {
204 MaintainContentLengthPrefsForDateChange(
205 original_update_
.Get(), received_update_
.Get(),
206 days_since_last_update
);
210 // Update the lengths for the current day.
211 void Add(int original_content_length
, int received_content_length
) {
213 kNumDaysInHistory
- 1, original_content_length
, original_update_
.Get());
215 kNumDaysInHistory
- 1, received_content_length
, received_update_
.Get());
218 int64
GetOriginalListPrefValue(size_t index
) {
219 return ListPrefInt64Value(*original_update_
, index
);
221 int64
GetReceivedListPrefValue(size_t index
) {
222 return ListPrefInt64Value(*received_update_
, index
);
226 const char* pref_original_
;
227 const char* pref_received_
;
228 ListPrefUpdate original_update_
;
229 ListPrefUpdate received_update_
;
233 #endif // defined(OS_ANDROID) || defined(OS_IOS)
235 namespace chrome_browser_net
{
237 #if defined(OS_ANDROID) || defined(OS_IOS)
238 void UpdateContentLengthPrefsForDataReductionProxy(
239 int received_content_length
, int original_content_length
,
240 bool with_data_reduction_proxy_enabled
, bool via_data_reduction_proxy
,
241 base::Time now
, PrefService
* prefs
) {
242 // TODO(bengr): Remove this check once the underlying cause of
243 // http://crbug.com/287821 is fixed. For now, only continue if the current
244 // year is reported as being between 1972 and 2970.
245 base::TimeDelta time_since_unix_epoch
= now
- base::Time::UnixEpoch();
246 const int kMinDaysSinceUnixEpoch
= 365 * 2; // 2 years.
247 const int kMaxDaysSinceUnixEpoch
= 365 * 1000; // 1000 years.
248 if (time_since_unix_epoch
.InDays() < kMinDaysSinceUnixEpoch
||
249 time_since_unix_epoch
.InDays() > kMaxDaysSinceUnixEpoch
) {
253 // Determine how many days it has been since the last update.
254 int64 then_internal
= prefs
->GetInt64(
255 prefs::kDailyHttpContentLengthLastUpdateDate
);
256 // Local midnight could have been shifted due to time zone change.
257 base::Time then_midnight
=
258 base::Time::FromInternalValue(then_internal
).LocalMidnight();
259 base::Time midnight
= now
.LocalMidnight();
260 int days_since_last_update
= (midnight
- then_midnight
).InDays();
262 // Each day, we calculate the total number of bytes received and the total
263 // size of all corresponding resources before any data-reducing recompression
264 // is applied. These values are used to compute the data savings realized
265 // by applying our compression techniques. Totals for the last
266 // |kNumDaysInHistory| days are maintained.
267 DailyDataSavingUpdate
total(
268 prefs::kDailyHttpOriginalContentLength
,
269 prefs::kDailyHttpReceivedContentLength
,
271 total
.UpdateForDataChange(days_since_last_update
);
273 DailyDataSavingUpdate
proxy_enabled(
274 prefs::kDailyOriginalContentLengthWithDataReductionProxyEnabled
,
275 prefs::kDailyContentLengthWithDataReductionProxyEnabled
,
277 proxy_enabled
.UpdateForDataChange(days_since_last_update
);
279 DailyDataSavingUpdate
via_proxy(
280 prefs::kDailyOriginalContentLengthViaDataReductionProxy
,
281 prefs::kDailyContentLengthViaDataReductionProxy
,
283 via_proxy
.UpdateForDataChange(days_since_last_update
);
285 total
.Add(original_content_length
, received_content_length
);
286 if (with_data_reduction_proxy_enabled
) {
287 proxy_enabled
.Add(original_content_length
, received_content_length
);
288 // Ignore cases, if exist, when
289 // "with_data_reduction_proxy_enabled == false", and
290 // "via_data_reduction_proxy == true"
291 if (via_data_reduction_proxy
) {
292 via_proxy
.Add(original_content_length
, received_content_length
);
296 if (days_since_last_update
) {
297 // Record the last update time in microseconds in UTC.
298 prefs
->SetInt64(prefs::kDailyHttpContentLengthLastUpdateDate
,
299 midnight
.ToInternalValue());
301 // A new day. Report the previous day's data if exists. We'll lose usage
302 // data if the last time Chrome was run was more than a day ago.
303 // Here, we prefer collecting less data but the collected data is
304 // associated with an accurate date.
305 if (days_since_last_update
== 1) {
306 // The previous day's data point is the second one from the tail.
307 RecordDailyContentLengthHistograms(
308 total
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
309 total
.GetReceivedListPrefValue(kNumDaysInHistory
- 2),
310 proxy_enabled
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
311 proxy_enabled
.GetReceivedListPrefValue(kNumDaysInHistory
- 2),
312 via_proxy
.GetOriginalListPrefValue(kNumDaysInHistory
- 2),
313 via_proxy
.GetReceivedListPrefValue(kNumDaysInHistory
- 2));
317 #endif // defined(OS_ANDROID) || defined(OS_IOS)
319 void UpdateContentLengthPrefs(
320 int received_content_length
, int original_content_length
,
321 bool with_data_reduction_proxy_enabled
, bool via_data_reduction_proxy
,
322 PrefService
* prefs
) {
323 int64 total_received
= prefs
->GetInt64(prefs::kHttpReceivedContentLength
);
324 int64 total_original
= prefs
->GetInt64(prefs::kHttpOriginalContentLength
);
325 total_received
+= received_content_length
;
326 total_original
+= original_content_length
;
327 prefs
->SetInt64(prefs::kHttpReceivedContentLength
, total_received
);
328 prefs
->SetInt64(prefs::kHttpOriginalContentLength
, total_original
);
330 #if defined(OS_ANDROID) || defined(OS_IOS)
331 UpdateContentLengthPrefsForDataReductionProxy(
332 received_content_length
,
333 original_content_length
,
334 with_data_reduction_proxy_enabled
,
335 via_data_reduction_proxy
,
338 #endif // defined(OS_ANDROID) || defined(OS_IOS)
342 } // namespace chrome_browser_net