Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(259)

Side by Side Diff: components/data_usage/android/traffic_stats_amortizer.cc

Issue 1390993005: Amortize data usage using TrafficStats on Android. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@data_use_buffering
Patch Set: Polished and cleaned up tests Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_usage/android/traffic_stats_amortizer.h"
6
7 #include <algorithm> // For std::min.
8 #include <cmath> // For std::modf.
9
10 #include "base/location.h"
11 #include "base/time/default_tick_clock.h"
12 #include "base/timer/timer.h"
13 #include "components/data_usage/core/data_use.h"
14 #include "net/android/traffic_stats.h"
15
16 namespace data_usage {
17 namespace android {
18
19 namespace {
20
21 // Convenience typedef.
22 typedef std::vector<std::pair<linked_ptr<DataUse>,
23 DataUseAmortizer::AmortizationCompleteCallback>>
24 DataUseBuffer;
25
26 // The delay between receiving DataUse and querying TrafficStats byte counts for
27 // amortization.
28 // TODO(sclittle): Control this with a field trial parameter.
29 const int64_t kDefaultTrafficStatsQueryDelayMs = 50;
30
31 // The longest amount of time that an amortization run can be delayed for.
32 // TODO(sclittle): Control this with a field trial parameter.
33 const int64_t kDefaultMaxAmortizationDelayMs = 500;
34
35 // The maximum allowed size of the DataUse buffer. If the buffer ever exceeds
36 // this size, then DataUse will be amortized immediately and the buffer will be
37 // flushed.
38 // TODO(sclittle): Control this with a field trial parameter.
39 const size_t kDefaultMaxDataUseBufferSize = 128;
40
41 // Scales |bytes| by |ratio|, using |remainder| to hold the running rounding
42 // error. |bytes| must be non-negative, and multiplying |bytes| by |ratio| must
43 // yield a number that's representable within the bounds of a non-negative
44 // int64_t.
45 int64_t ScaleByteCount(int64_t bytes, double ratio, double* remainder) {
46 DCHECK_GE(bytes, 0);
47 DCHECK_GE(ratio, 0.0);
48 DCHECK_LE(ratio, static_cast<double>(INT64_MAX));
49 DCHECK_GE(*remainder, 0.0);
50 DCHECK_LT(*remainder, 1.0);
51
52 double intpart;
53 *remainder =
54 std::modf(static_cast<double>(bytes) * ratio + (*remainder), &intpart);
55
56 DCHECK_GE(intpart, 0.0);
57 DCHECK_LE(intpart, static_cast<double>(INT64_MAX));
58 DCHECK_GE(*remainder, 0.0);
59 DCHECK_LT(*remainder, 1.0);
60
61 // Due to floating point error, casting the double |intpart| to an int64_t
62 // could cause it to overflow, even though it's already been checked to be
63 // less than the double representation of INT64_MAX. If this happens, cap the
64 // scaled value at INT64_MAX.
65 uint64_t scaled_bytes = std::min(static_cast<uint64_t>(intpart),
66 static_cast<uint64_t>(INT64_MAX));
67 return static_cast<int64_t>(scaled_bytes);
68 }
69
70 // Amortize the difference between |desired_post_amortization_total| and
71 // |pre_amortization_total| into each of the DataUse objects in
72 // |data_use_sequence| by scaling the byte counts determined by the
73 // |get_byte_count_fn| function (e.g. tx_bytes, rx_bytes) for each DataUse
74 // appropriately.
75 void AmortizeByteCountSequence(DataUseBuffer* data_use_sequence,
76 int64_t* (*get_byte_count_fn)(DataUse*),
77 int64_t pre_amortization_total,
78 int64_t desired_post_amortization_total) {
79 DCHECK_GE(pre_amortization_total, 0);
80 DCHECK_GE(desired_post_amortization_total, 0);
81
82 if (pre_amortization_total == 0) {
83 // TODO(sclittle): If |desired_post_amortization_total| is non-zero, this
84 // could be ignoring overhead that should be amortized in somehow. Handle
85 // this case gracefully.
86 return;
87 }
88
89 const double ratio = static_cast<double>(desired_post_amortization_total) /
90 static_cast<double>(pre_amortization_total);
91
92 double remainder = 0.0;
93 for (auto& data_use_buffer_pair : *data_use_sequence) {
94 int64_t* byte_count = get_byte_count_fn(data_use_buffer_pair.first.get());
95 *byte_count = ScaleByteCount(*byte_count, ratio, &remainder);
96 // TODO(sclittle): Record UMA about values before vs. after amortization.
97 }
98 }
99
100 int64_t* GetTxBytes(DataUse* data_use) {
101 return &data_use->tx_bytes;
102 }
103 int64_t* GetRxBytes(DataUse* data_use) {
104 return &data_use->rx_bytes;
105 }
106
107 // Amortize the difference between |desired_post_amortization_total_tx_bytes|
bengr 2015/11/11 15:41:38 Amortize -> Amortizes
sclittle 2015/11/11 20:02:20 Done.
108 // and |pre_amortization_total_tx_bytes| into each of the DataUse objects in
109 // |data_use_sequence| by scaling the DataUse's |tx_bytes| appropriately. Does
110 // the same with the |rx_bytes| using those respective parameters.
111 void AmortizeDataUseSequence(DataUseBuffer* data_use_sequence,
112 int64_t pre_amortization_total_tx_bytes,
113 int64_t desired_post_amortization_total_tx_bytes,
114 int64_t pre_amortization_total_rx_bytes,
115 int64_t desired_post_amortization_total_rx_bytes) {
116 DCHECK(data_use_sequence);
117 DCHECK(!data_use_sequence->empty());
118
119 AmortizeByteCountSequence(data_use_sequence, &GetTxBytes,
120 pre_amortization_total_tx_bytes,
121 desired_post_amortization_total_tx_bytes);
122
123 AmortizeByteCountSequence(data_use_sequence, &GetRxBytes,
124 pre_amortization_total_rx_bytes,
125 desired_post_amortization_total_rx_bytes);
126 }
127
128 } // namespace
129
130 TrafficStatsAmortizer::TrafficStatsAmortizer()
131 : TrafficStatsAmortizer(
132 scoped_ptr<base::TickClock>(new base::DefaultTickClock()),
133 scoped_ptr<base::Timer>(new base::Timer(false, false)),
134 base::TimeDelta::FromMilliseconds(kDefaultTrafficStatsQueryDelayMs),
135 base::TimeDelta::FromMilliseconds(kDefaultMaxAmortizationDelayMs),
136 kDefaultMaxDataUseBufferSize) {}
137
138 TrafficStatsAmortizer::~TrafficStatsAmortizer() {}
139
140 void TrafficStatsAmortizer::AmortizeDataUse(
141 scoped_ptr<DataUse> data_use,
142 const AmortizationCompleteCallback& callback) {
143 DCHECK(thread_checker_.CalledOnValidThread());
144 DCHECK(!callback.is_null());
145 int64_t tx_bytes = data_use->tx_bytes, rx_bytes = data_use->rx_bytes;
146
147 // TODO(sclittle): Combine consecutive buffered DataUse objects that are
148 // identical except for byte counts and have the same callback.
149 buffered_data_use_.push_back(
150 std::make_pair(linked_ptr<DataUse>(data_use.release()), callback));
151
152 AddPreAmortizationBytes(tx_bytes, rx_bytes);
153 }
154
155 void TrafficStatsAmortizer::OnExtraBytes(int64_t extra_tx_bytes,
156 int64_t extra_rx_bytes) {
157 DCHECK(thread_checker_.CalledOnValidThread());
158 AddPreAmortizationBytes(extra_tx_bytes, extra_rx_bytes);
159 }
160
161 base::WeakPtr<TrafficStatsAmortizer> TrafficStatsAmortizer::GetWeakPtr() {
162 DCHECK(thread_checker_.CalledOnValidThread());
163 return weak_ptr_factory_.GetWeakPtr();
164 }
165
166 TrafficStatsAmortizer::TrafficStatsAmortizer(
167 scoped_ptr<base::TickClock> tick_clock,
168 scoped_ptr<base::Timer> traffic_stats_query_timer,
169 const base::TimeDelta& traffic_stats_query_delay,
170 const base::TimeDelta& max_amortization_delay,
171 size_t max_data_use_buffer_size)
172 : tick_clock_(tick_clock.Pass()),
173 traffic_stats_query_timer_(traffic_stats_query_timer.Pass()),
174 traffic_stats_query_delay_(traffic_stats_query_delay),
175 max_amortization_delay_(max_amortization_delay),
176 max_data_use_buffer_size_(max_data_use_buffer_size),
177 is_amortization_in_progress_(false),
178 are_last_amortization_traffic_stats_available_(false),
179 last_amortization_traffic_stats_tx_bytes_(-1),
180 last_amortization_traffic_stats_rx_bytes_(-1),
181 pre_amortization_tx_bytes_(0),
182 pre_amortization_rx_bytes_(0),
183 weak_ptr_factory_(this) {}
184
185 bool TrafficStatsAmortizer::QueryTrafficStats(int64_t* tx_bytes,
186 int64_t* rx_bytes) const {
187 DCHECK(thread_checker_.CalledOnValidThread());
188 return net::android::traffic_stats::GetCurrentUidTxBytes(tx_bytes) &&
189 net::android::traffic_stats::GetCurrentUidRxBytes(rx_bytes);
190 }
191
192 void TrafficStatsAmortizer::AddPreAmortizationBytes(int64_t tx_bytes,
193 int64_t rx_bytes) {
194 DCHECK(thread_checker_.CalledOnValidThread());
195 DCHECK_GE(tx_bytes, 0);
196 DCHECK_GE(rx_bytes, 0);
197 base::TimeTicks now_ticks = tick_clock_->NowTicks();
198
199 if (!is_amortization_in_progress_) {
200 is_amortization_in_progress_ = true;
201 current_amortization_run_start_time_ = now_ticks;
202 }
203
204 pre_amortization_tx_bytes_ += tx_bytes;
205 pre_amortization_rx_bytes_ += rx_bytes;
206
207 if (buffered_data_use_.size() > max_data_use_buffer_size_) {
208 // Enforce a maximum limit on the size of |buffered_data_use_| to avoid
209 // hogging memory. Note that this will likely cause the post-amortization
210 // byte counts calculated here to be less accurate than if the amortizer
211 // waited to perform amortization.
212 // TODO(sclittle): Record UMA about how often this occurs.
213 traffic_stats_query_timer_->Stop();
214 AmortizeNow();
215 return;
216 }
217
218 // Cap any amortization delay to |max_amortization_delay_|. Note that if
219 // |max_amortization_delay_| comes earlier, then this will likely cause the
220 // post-amortization byte counts calculated here to be less accurate than if
221 // the amortizer waited to perform amortization.
222 // TODO(sclittle): Record UMA about how often |max_amortization_delay_| comes
223 // earlier.
224 base::TimeDelta query_delay = std::min(
225 traffic_stats_query_delay_, current_amortization_run_start_time_ +
226 max_amortization_delay_ - now_ticks);
227
228 // Set the timer to query TrafficStats and amortize after a delay, so that
229 // it's more likely that TrafficStats will be queried when the network is
230 // idle. If the timer was already set, then this overrides the previous delay.
231 traffic_stats_query_timer_->Start(
232 FROM_HERE, query_delay,
233 base::Bind(&TrafficStatsAmortizer::AmortizeNow, GetWeakPtr()));
234 }
235
236 void TrafficStatsAmortizer::AmortizeNow() {
237 DCHECK(thread_checker_.CalledOnValidThread());
238
239 int64_t current_traffic_stats_tx_bytes = -1;
240 int64_t current_traffic_stats_rx_bytes = -1;
241 bool are_current_traffic_stats_available = QueryTrafficStats(
242 &current_traffic_stats_tx_bytes, &current_traffic_stats_rx_bytes);
243
244 if (are_current_traffic_stats_available &&
245 are_last_amortization_traffic_stats_available_ &&
246 !buffered_data_use_.empty()) {
247 // These TrafficStats byte counts are guaranteed to increase monotonically
248 // since device boot.
249 DCHECK_GE(current_traffic_stats_tx_bytes,
250 last_amortization_traffic_stats_tx_bytes_);
251 DCHECK_GE(current_traffic_stats_rx_bytes,
252 last_amortization_traffic_stats_rx_bytes_);
253
254 int64_t desired_post_amortization_total_tx_bytes =
255 current_traffic_stats_tx_bytes -
256 last_amortization_traffic_stats_tx_bytes_;
257 int64_t desired_post_amortization_total_rx_bytes =
258 current_traffic_stats_rx_bytes -
259 last_amortization_traffic_stats_rx_bytes_;
260
261 AmortizeDataUseSequence(&buffered_data_use_, pre_amortization_tx_bytes_,
262 desired_post_amortization_total_tx_bytes,
263 pre_amortization_rx_bytes_,
264 desired_post_amortization_total_rx_bytes);
265 }
266
267 // TODO(sclittle): Record some UMA about the delay before amortizing and how
268 // big the buffer was before amortizing.
269
270 // Reset state now that the amortization run has finished.
271 is_amortization_in_progress_ = false;
272 current_amortization_run_start_time_ = base::TimeTicks();
273
274 are_last_amortization_traffic_stats_available_ =
275 are_current_traffic_stats_available;
276 last_amortization_traffic_stats_tx_bytes_ = current_traffic_stats_tx_bytes;
277 last_amortization_traffic_stats_rx_bytes_ = current_traffic_stats_rx_bytes;
278
279 pre_amortization_tx_bytes_ = 0;
280 pre_amortization_rx_bytes_ = 0;
281
282 // Pass post-amortization DataUse objects to their respective callbacks.
283 DataUseBuffer data_use_sequence;
284 data_use_sequence.swap(buffered_data_use_);
285 for (auto& data_use_buffer_pair : data_use_sequence) {
286 scoped_ptr<DataUse> data_use(data_use_buffer_pair.first.release());
287 data_use_buffer_pair.second.Run(data_use.Pass());
288 }
289 }
290
291 } // namespace android
292 } // namespace data_usage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698