blob: 4a4accbba502e4548edb028ab08aa8bed53131d5 [file] [log] [blame]
Jeff Sharkey75279902011-05-24 18:39:45 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
Jeff Sharkeya63ba592011-07-19 23:47:12 -070019import static android.net.NetworkStats.IFACE_ALL;
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -070020import static android.net.NetworkStats.SET_DEFAULT;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070021import static android.net.NetworkStats.TAG_NONE;
22import static android.net.NetworkStats.UID_ALL;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070023import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray;
24import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLongArray;
25import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
26import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070027import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070028import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080029import static android.text.format.DateUtils.SECOND_IN_MILLIS;
Jeff Sharkey63abc372012-01-11 18:38:16 -080030import static com.android.internal.util.ArrayUtils.total;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070031
Jeff Sharkey75279902011-05-24 18:39:45 -070032import android.os.Parcel;
33import android.os.Parcelable;
Jeff Sharkey69b0f632011-09-11 17:33:14 -070034import android.util.MathUtils;
Jeff Sharkey75279902011-05-24 18:39:45 -070035
Jeff Sharkey63abc372012-01-11 18:38:16 -080036import com.android.internal.util.IndentingPrintWriter;
37
Jeff Sharkey75279902011-05-24 18:39:45 -070038import java.io.CharArrayWriter;
39import java.io.DataInputStream;
40import java.io.DataOutputStream;
41import java.io.IOException;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080042import java.io.PrintWriter;
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -070043import java.net.ProtocolException;
Jeff Sharkey75279902011-05-24 18:39:45 -070044import java.util.Arrays;
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -070045import java.util.Random;
Jeff Sharkey75279902011-05-24 18:39:45 -070046
47/**
48 * Collection of historical network statistics, recorded into equally-sized
49 * "buckets" in time. Internally it stores data in {@code long} series for more
50 * efficient persistence.
51 * <p>
52 * Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for
53 * {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is
54 * sorted at all times.
55 *
56 * @hide
57 */
58public class NetworkStatsHistory implements Parcelable {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070059 private static final int VERSION_INIT = 1;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070060 private static final int VERSION_ADD_PACKETS = 2;
Jeff Sharkey558a2322011-08-24 15:42:09 -070061 private static final int VERSION_ADD_ACTIVE = 3;
Jeff Sharkey75279902011-05-24 18:39:45 -070062
Jeff Sharkey558a2322011-08-24 15:42:09 -070063 public static final int FIELD_ACTIVE_TIME = 0x01;
64 public static final int FIELD_RX_BYTES = 0x02;
65 public static final int FIELD_RX_PACKETS = 0x04;
66 public static final int FIELD_TX_BYTES = 0x08;
67 public static final int FIELD_TX_PACKETS = 0x10;
68 public static final int FIELD_OPERATIONS = 0x20;
Jeff Sharkey75279902011-05-24 18:39:45 -070069
Jeff Sharkey63d27a92011-08-03 17:04:22 -070070 public static final int FIELD_ALL = 0xFFFFFFFF;
71
72 private long bucketDuration;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070073 private int bucketCount;
74 private long[] bucketStart;
Jeff Sharkey558a2322011-08-24 15:42:09 -070075 private long[] activeTime;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070076 private long[] rxBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070077 private long[] rxPackets;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070078 private long[] txBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070079 private long[] txPackets;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070080 private long[] operations;
Jeff Sharkey63abc372012-01-11 18:38:16 -080081 private long totalBytes;
Jeff Sharkey75279902011-05-24 18:39:45 -070082
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070083 public static class Entry {
Jeff Sharkey63d27a92011-08-03 17:04:22 -070084 public static final long UNKNOWN = -1;
85
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070086 public long bucketDuration;
Jeff Sharkey558a2322011-08-24 15:42:09 -070087 public long bucketStart;
88 public long activeTime;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070089 public long rxBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070090 public long rxPackets;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070091 public long txBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070092 public long txPackets;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070093 public long operations;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070094 }
Jeff Sharkey75279902011-05-24 18:39:45 -070095
Jeff Sharkeyd2a45872011-05-28 20:56:34 -070096 public NetworkStatsHistory(long bucketDuration) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -070097 this(bucketDuration, 10, FIELD_ALL);
Jeff Sharkey4a971222011-06-11 22:16:55 -070098 }
99
100 public NetworkStatsHistory(long bucketDuration, int initialSize) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700101 this(bucketDuration, initialSize, FIELD_ALL);
102 }
103
104 public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700105 this.bucketDuration = bucketDuration;
Jeff Sharkey4a971222011-06-11 22:16:55 -0700106 bucketStart = new long[initialSize];
Jeff Sharkey558a2322011-08-24 15:42:09 -0700107 if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700108 if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
109 if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
110 if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
111 if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
112 if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
Jeff Sharkey4a971222011-06-11 22:16:55 -0700113 bucketCount = 0;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800114 totalBytes = 0;
115 }
116
117 public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
118 this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
119 recordEntireHistory(existing);
Jeff Sharkey75279902011-05-24 18:39:45 -0700120 }
121
122 public NetworkStatsHistory(Parcel in) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700123 bucketDuration = in.readLong();
124 bucketStart = readLongArray(in);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700125 activeTime = readLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700126 rxBytes = readLongArray(in);
127 rxPackets = readLongArray(in);
128 txBytes = readLongArray(in);
129 txPackets = readLongArray(in);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700130 operations = readLongArray(in);
Jeff Sharkey75279902011-05-24 18:39:45 -0700131 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800132 totalBytes = in.readLong();
Jeff Sharkey75279902011-05-24 18:39:45 -0700133 }
134
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700135 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700136 public void writeToParcel(Parcel out, int flags) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700137 out.writeLong(bucketDuration);
138 writeLongArray(out, bucketStart, bucketCount);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700139 writeLongArray(out, activeTime, bucketCount);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700140 writeLongArray(out, rxBytes, bucketCount);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700141 writeLongArray(out, rxPackets, bucketCount);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700142 writeLongArray(out, txBytes, bucketCount);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700143 writeLongArray(out, txPackets, bucketCount);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700144 writeLongArray(out, operations, bucketCount);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800145 out.writeLong(totalBytes);
Jeff Sharkey75279902011-05-24 18:39:45 -0700146 }
147
148 public NetworkStatsHistory(DataInputStream in) throws IOException {
149 final int version = in.readInt();
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700150 switch (version) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700151 case VERSION_INIT: {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700152 bucketDuration = in.readLong();
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700153 bucketStart = readFullLongArray(in);
154 rxBytes = readFullLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700155 rxPackets = new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700156 txBytes = readFullLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700157 txPackets = new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700158 operations = new long[bucketStart.length];
159 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800160 totalBytes = total(rxBytes) + total(txBytes);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700161 break;
162 }
Jeff Sharkey558a2322011-08-24 15:42:09 -0700163 case VERSION_ADD_PACKETS:
164 case VERSION_ADD_ACTIVE: {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700165 bucketDuration = in.readLong();
166 bucketStart = readVarLongArray(in);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700167 activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in)
168 : new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700169 rxBytes = readVarLongArray(in);
170 rxPackets = readVarLongArray(in);
171 txBytes = readVarLongArray(in);
172 txPackets = readVarLongArray(in);
173 operations = readVarLongArray(in);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700174 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800175 totalBytes = total(rxBytes) + total(txBytes);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700176 break;
177 }
178 default: {
179 throw new ProtocolException("unexpected version: " + version);
180 }
181 }
Jeff Sharkeyb0a579f2012-11-02 14:48:20 -0700182
183 if (bucketStart.length != bucketCount || rxBytes.length != bucketCount
184 || rxPackets.length != bucketCount || txBytes.length != bucketCount
185 || txPackets.length != bucketCount || operations.length != bucketCount) {
186 throw new ProtocolException("Mismatched history lengths");
187 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700188 }
189
190 public void writeToStream(DataOutputStream out) throws IOException {
Jeff Sharkey558a2322011-08-24 15:42:09 -0700191 out.writeInt(VERSION_ADD_ACTIVE);
Jeff Sharkey75279902011-05-24 18:39:45 -0700192 out.writeLong(bucketDuration);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700193 writeVarLongArray(out, bucketStart, bucketCount);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700194 writeVarLongArray(out, activeTime, bucketCount);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700195 writeVarLongArray(out, rxBytes, bucketCount);
196 writeVarLongArray(out, rxPackets, bucketCount);
197 writeVarLongArray(out, txBytes, bucketCount);
198 writeVarLongArray(out, txPackets, bucketCount);
199 writeVarLongArray(out, operations, bucketCount);
Jeff Sharkey75279902011-05-24 18:39:45 -0700200 }
201
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700202 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700203 public int describeContents() {
204 return 0;
205 }
206
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700207 public int size() {
208 return bucketCount;
209 }
210
211 public long getBucketDuration() {
212 return bucketDuration;
213 }
214
Jeff Sharkey434962e2011-07-12 20:20:56 -0700215 public long getStart() {
216 if (bucketCount > 0) {
217 return bucketStart[0];
218 } else {
219 return Long.MAX_VALUE;
220 }
221 }
222
223 public long getEnd() {
224 if (bucketCount > 0) {
225 return bucketStart[bucketCount - 1] + bucketDuration;
226 } else {
227 return Long.MIN_VALUE;
228 }
229 }
230
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700231 /**
Jeff Sharkey63abc372012-01-11 18:38:16 -0800232 * Return total bytes represented by this history.
233 */
234 public long getTotalBytes() {
235 return totalBytes;
236 }
237
238 /**
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700239 * Return index of bucket that contains or is immediately before the
240 * requested time.
241 */
242 public int getIndexBefore(long time) {
243 int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
244 if (index < 0) {
245 index = (~index) - 1;
246 } else {
247 index -= 1;
248 }
249 return MathUtils.constrain(index, 0, bucketCount - 1);
250 }
251
252 /**
253 * Return index of bucket that contains or is immediately after the
254 * requested time.
255 */
256 public int getIndexAfter(long time) {
257 int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
258 if (index < 0) {
259 index = ~index;
260 } else {
261 index += 1;
262 }
263 return MathUtils.constrain(index, 0, bucketCount - 1);
264 }
265
266 /**
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700267 * Return specific stats entry.
268 */
269 public Entry getValues(int i, Entry recycle) {
270 final Entry entry = recycle != null ? recycle : new Entry();
271 entry.bucketStart = bucketStart[i];
272 entry.bucketDuration = bucketDuration;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700273 entry.activeTime = getLong(activeTime, i, UNKNOWN);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700274 entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
275 entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
276 entry.txBytes = getLong(txBytes, i, UNKNOWN);
277 entry.txPackets = getLong(txPackets, i, UNKNOWN);
278 entry.operations = getLong(operations, i, UNKNOWN);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700279 return entry;
280 }
281
Jeff Sharkey75279902011-05-24 18:39:45 -0700282 /**
283 * Record that data traffic occurred in the given time range. Will
284 * distribute across internal buckets, creating new buckets as needed.
285 */
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700286 @Deprecated
287 public void recordData(long start, long end, long rxBytes, long txBytes) {
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700288 recordData(start, end, new NetworkStats.Entry(
289 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700290 }
291
292 /**
293 * Record that data traffic occurred in the given time range. Will
294 * distribute across internal buckets, creating new buckets as needed.
295 */
296 public void recordData(long start, long end, NetworkStats.Entry entry) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800297 long rxBytes = entry.rxBytes;
298 long rxPackets = entry.rxPackets;
299 long txBytes = entry.txBytes;
300 long txPackets = entry.txPackets;
301 long operations = entry.operations;
302
303 if (entry.isNegative()) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700304 throw new IllegalArgumentException("tried recording negative data");
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700305 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800306 if (entry.isEmpty()) {
Jeff Sharkey367d15a2011-09-22 14:59:51 -0700307 return;
308 }
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700309
Jeff Sharkey75279902011-05-24 18:39:45 -0700310 // create any buckets needed by this range
311 ensureBuckets(start, end);
312
313 // distribute data usage into buckets
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700314 long duration = end - start;
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700315 final int startIndex = getIndexAfter(end);
316 for (int i = startIndex; i >= 0; i--) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700317 final long curStart = bucketStart[i];
318 final long curEnd = curStart + bucketDuration;
319
320 // bucket is older than record; we're finished
321 if (curEnd < start) break;
322 // bucket is newer than record; keep looking
323 if (curStart > end) continue;
324
325 final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700326 if (overlap <= 0) continue;
327
328 // integer math each time is faster than floating point
Jeff Sharkey63abc372012-01-11 18:38:16 -0800329 final long fracRxBytes = rxBytes * overlap / duration;
330 final long fracRxPackets = rxPackets * overlap / duration;
331 final long fracTxBytes = txBytes * overlap / duration;
332 final long fracTxPackets = txPackets * overlap / duration;
333 final long fracOperations = operations * overlap / duration;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700334
Jeff Sharkey558a2322011-08-24 15:42:09 -0700335 addLong(activeTime, i, overlap);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800336 addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
337 addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
338 addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
339 addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
340 addLong(this.operations, i, fracOperations); operations -= fracOperations;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700341
342 duration -= overlap;
Jeff Sharkey75279902011-05-24 18:39:45 -0700343 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800344
345 totalBytes += entry.rxBytes + entry.txBytes;
Jeff Sharkey75279902011-05-24 18:39:45 -0700346 }
347
348 /**
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700349 * Record an entire {@link NetworkStatsHistory} into this history. Usually
350 * for combining together stats for external reporting.
351 */
352 public void recordEntireHistory(NetworkStatsHistory input) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700353 recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE);
354 }
355
356 /**
357 * Record given {@link NetworkStatsHistory} into this history, copying only
358 * buckets that atomically occur in the inclusive time range. Doesn't
359 * interpolate across partial buckets.
360 */
361 public void recordHistory(NetworkStatsHistory input, long start, long end) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700362 final NetworkStats.Entry entry = new NetworkStats.Entry(
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700363 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700364 for (int i = 0; i < input.bucketCount; i++) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700365 final long bucketStart = input.bucketStart[i];
366 final long bucketEnd = bucketStart + input.bucketDuration;
367
368 // skip when bucket is outside requested range
369 if (bucketStart < start || bucketEnd > end) continue;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700370
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700371 entry.rxBytes = getLong(input.rxBytes, i, 0L);
372 entry.rxPackets = getLong(input.rxPackets, i, 0L);
373 entry.txBytes = getLong(input.txBytes, i, 0L);
374 entry.txPackets = getLong(input.txPackets, i, 0L);
375 entry.operations = getLong(input.operations, i, 0L);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700376
Jeff Sharkey70c70532012-05-16 14:51:19 -0700377 recordData(bucketStart, bucketEnd, entry);
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700378 }
379 }
380
381 /**
Jeff Sharkey75279902011-05-24 18:39:45 -0700382 * Ensure that buckets exist for given time range, creating as needed.
383 */
384 private void ensureBuckets(long start, long end) {
385 // normalize incoming range to bucket boundaries
386 start -= start % bucketDuration;
387 end += (bucketDuration - (end % bucketDuration)) % bucketDuration;
388
389 for (long now = start; now < end; now += bucketDuration) {
390 // try finding existing bucket
391 final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now);
392 if (index < 0) {
393 // bucket missing, create and insert
394 insertBucket(~index, now);
395 }
396 }
397 }
398
399 /**
400 * Insert new bucket at requested index and starting time.
401 */
402 private void insertBucket(int index, long start) {
403 // create more buckets when needed
Jeff Sharkey4a971222011-06-11 22:16:55 -0700404 if (bucketCount >= bucketStart.length) {
405 final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
Jeff Sharkey75279902011-05-24 18:39:45 -0700406 bucketStart = Arrays.copyOf(bucketStart, newLength);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700407 if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700408 if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
409 if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
410 if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
411 if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength);
412 if (operations != null) operations = Arrays.copyOf(operations, newLength);
Jeff Sharkey75279902011-05-24 18:39:45 -0700413 }
414
415 // create gap when inserting bucket in middle
416 if (index < bucketCount) {
417 final int dstPos = index + 1;
418 final int length = bucketCount - index;
419
420 System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700421 if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700422 if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
423 if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
424 if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
425 if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length);
426 if (operations != null) System.arraycopy(operations, index, operations, dstPos, length);
Jeff Sharkey75279902011-05-24 18:39:45 -0700427 }
428
429 bucketStart[index] = start;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700430 setLong(activeTime, index, 0L);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700431 setLong(rxBytes, index, 0L);
432 setLong(rxPackets, index, 0L);
433 setLong(txBytes, index, 0L);
434 setLong(txPackets, index, 0L);
435 setLong(operations, index, 0L);
Jeff Sharkey75279902011-05-24 18:39:45 -0700436 bucketCount++;
437 }
438
439 /**
440 * Remove buckets older than requested cutoff.
441 */
Jeff Sharkey63abc372012-01-11 18:38:16 -0800442 @Deprecated
Jeff Sharkey75279902011-05-24 18:39:45 -0700443 public void removeBucketsBefore(long cutoff) {
444 int i;
445 for (i = 0; i < bucketCount; i++) {
446 final long curStart = bucketStart[i];
447 final long curEnd = curStart + bucketDuration;
448
449 // cutoff happens before or during this bucket; everything before
450 // this bucket should be removed.
451 if (curEnd > cutoff) break;
452 }
453
454 if (i > 0) {
455 final int length = bucketStart.length;
456 bucketStart = Arrays.copyOfRange(bucketStart, i, length);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700457 if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700458 if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
459 if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
460 if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
461 if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
462 if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
Jeff Sharkey75279902011-05-24 18:39:45 -0700463 bucketCount -= i;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800464
465 // TODO: subtract removed values from totalBytes
Jeff Sharkey75279902011-05-24 18:39:45 -0700466 }
467 }
468
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700469 /**
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700470 * Return interpolated data usage across the requested range. Interpolates
471 * across buckets, so values may be rounded slightly.
472 */
Jeff Sharkey434962e2011-07-12 20:20:56 -0700473 public Entry getValues(long start, long end, Entry recycle) {
474 return getValues(start, end, Long.MAX_VALUE, recycle);
475 }
476
477 /**
478 * Return interpolated data usage across the requested range. Interpolates
479 * across buckets, so values may be rounded slightly.
480 */
481 public Entry getValues(long start, long end, long now, Entry recycle) {
482 final Entry entry = recycle != null ? recycle : new Entry();
Jeff Sharkey434962e2011-07-12 20:20:56 -0700483 entry.bucketDuration = end - start;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700484 entry.bucketStart = start;
485 entry.activeTime = activeTime != null ? 0 : UNKNOWN;
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700486 entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
487 entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
488 entry.txBytes = txBytes != null ? 0 : UNKNOWN;
489 entry.txPackets = txPackets != null ? 0 : UNKNOWN;
490 entry.operations = operations != null ? 0 : UNKNOWN;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700491
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700492 final int startIndex = getIndexAfter(end);
493 for (int i = startIndex; i >= 0; i--) {
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700494 final long curStart = bucketStart[i];
495 final long curEnd = curStart + bucketDuration;
496
Jeff Sharkey34c73ac2011-09-18 13:30:23 -0700497 // bucket is older than request; we're finished
498 if (curEnd <= start) break;
499 // bucket is newer than request; keep looking
500 if (curStart >= end) continue;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700501
Jeff Sharkey434962e2011-07-12 20:20:56 -0700502 // include full value for active buckets, otherwise only fractional
503 final boolean activeBucket = curStart < now && curEnd > now;
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700504 final long overlap;
505 if (activeBucket) {
506 overlap = bucketDuration;
507 } else {
508 final long overlapEnd = curEnd < end ? curEnd : end;
509 final long overlapStart = curStart > start ? curStart : start;
510 overlap = overlapEnd - overlapStart;
511 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700512 if (overlap <= 0) continue;
513
514 // integer math each time is faster than floating point
Jeff Sharkey558a2322011-08-24 15:42:09 -0700515 if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration;
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700516 if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
517 if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
518 if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
519 if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
520 if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700521 }
Jeff Sharkey434962e2011-07-12 20:20:56 -0700522 return entry;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700523 }
524
525 /**
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700526 * @deprecated only for temporary testing
527 */
528 @Deprecated
Jeff Sharkey293779f2011-10-05 23:31:57 -0700529 public void generateRandom(long start, long end, long bytes) {
530 final Random r = new Random();
531
532 final float fractionRx = r.nextFloat();
533 final long rxBytes = (long) (bytes * fractionRx);
534 final long txBytes = (long) (bytes * (1 - fractionRx));
535
536 final long rxPackets = rxBytes / 1024;
537 final long txPackets = txBytes / 1024;
538 final long operations = rxBytes / 2048;
539
540 generateRandom(start, end, rxBytes, rxPackets, txBytes, txPackets, operations, r);
541 }
542
543 /**
544 * @deprecated only for temporary testing
545 */
546 @Deprecated
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700547 public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
Jeff Sharkey293779f2011-10-05 23:31:57 -0700548 long txPackets, long operations, Random r) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700549 ensureBuckets(start, end);
550
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700551 final NetworkStats.Entry entry = new NetworkStats.Entry(
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700552 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
Jeff Sharkey04cd0e42011-08-05 11:09:46 -0700553 while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
554 || operations > 32) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700555 final long curStart = randomLong(r, start, end);
Jeff Sharkey293779f2011-10-05 23:31:57 -0700556 final long curEnd = curStart + randomLong(r, 0, (end - curStart) / 2);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700557
558 entry.rxBytes = randomLong(r, 0, rxBytes);
559 entry.rxPackets = randomLong(r, 0, rxPackets);
560 entry.txBytes = randomLong(r, 0, txBytes);
561 entry.txPackets = randomLong(r, 0, txPackets);
562 entry.operations = randomLong(r, 0, operations);
563
564 rxBytes -= entry.rxBytes;
565 rxPackets -= entry.rxPackets;
566 txBytes -= entry.txBytes;
567 txPackets -= entry.txPackets;
568 operations -= entry.operations;
Jeff Sharkeyf0ceede2011-08-02 17:22:34 -0700569
570 recordData(curStart, curEnd, entry);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700571 }
572 }
573
Jeff Sharkey293779f2011-10-05 23:31:57 -0700574 public static long randomLong(Random r, long start, long end) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700575 return (long) (start + (r.nextFloat() * (end - start)));
576 }
577
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800578 /**
579 * Quickly determine if this history intersects with given window.
580 */
581 public boolean intersects(long start, long end) {
582 final long dataStart = getStart();
583 final long dataEnd = getEnd();
584 if (start >= dataStart && start <= dataEnd) return true;
585 if (end >= dataStart && end <= dataEnd) return true;
586 if (dataStart >= start && dataStart <= end) return true;
587 if (dataEnd >= start && dataEnd <= end) return true;
588 return false;
589 }
590
Jeff Sharkey63abc372012-01-11 18:38:16 -0800591 public void dump(IndentingPrintWriter pw, boolean fullHistory) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800592 pw.print("NetworkStatsHistory: bucketDuration=");
593 pw.println(bucketDuration / SECOND_IN_MILLIS);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800594 pw.increaseIndent();
Jeff Sharkey350083e2011-06-29 10:45:16 -0700595
596 final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
597 if (start > 0) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800598 pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
Jeff Sharkey350083e2011-06-29 10:45:16 -0700599 }
600
601 for (int i = start; i < bucketCount; i++) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800602 pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
603 if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
604 if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
605 if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
606 if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
607 if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700608 pw.println();
Jeff Sharkey75279902011-05-24 18:39:45 -0700609 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800610
611 pw.decreaseIndent();
Jeff Sharkey75279902011-05-24 18:39:45 -0700612 }
613
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800614 public void dumpCheckin(PrintWriter pw) {
615 pw.print("d,");
616 pw.print(bucketDuration / SECOND_IN_MILLIS);
617 pw.println();
618
619 for (int i = 0; i < bucketCount; i++) {
620 pw.print("b,");
621 pw.print(bucketStart[i] / SECOND_IN_MILLIS); pw.print(',');
622 if (rxBytes != null) { pw.print(rxBytes[i]); } else { pw.print("*"); } pw.print(',');
623 if (rxPackets != null) { pw.print(rxPackets[i]); } else { pw.print("*"); } pw.print(',');
624 if (txBytes != null) { pw.print(txBytes[i]); } else { pw.print("*"); } pw.print(',');
625 if (txPackets != null) { pw.print(txPackets[i]); } else { pw.print("*"); } pw.print(',');
626 if (operations != null) { pw.print(operations[i]); } else { pw.print("*"); }
627 pw.println();
628 }
629 }
630
Jeff Sharkey75279902011-05-24 18:39:45 -0700631 @Override
632 public String toString() {
633 final CharArrayWriter writer = new CharArrayWriter();
Jeff Sharkey63abc372012-01-11 18:38:16 -0800634 dump(new IndentingPrintWriter(writer, " "), false);
Jeff Sharkey75279902011-05-24 18:39:45 -0700635 return writer.toString();
636 }
637
638 public static final Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() {
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700639 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700640 public NetworkStatsHistory createFromParcel(Parcel in) {
641 return new NetworkStatsHistory(in);
642 }
643
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700644 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700645 public NetworkStatsHistory[] newArray(int size) {
646 return new NetworkStatsHistory[size];
647 }
648 };
649
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700650 private static long getLong(long[] array, int i, long value) {
651 return array != null ? array[i] : value;
652 }
653
654 private static void setLong(long[] array, int i, long value) {
655 if (array != null) array[i] = value;
656 }
657
658 private static void addLong(long[] array, int i, long value) {
659 if (array != null) array[i] += value;
660 }
661
Jeff Sharkey63abc372012-01-11 18:38:16 -0800662 public int estimateResizeBuckets(long newBucketDuration) {
663 return (int) (size() * getBucketDuration() / newBucketDuration);
664 }
665
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700666 /**
667 * Utility methods for interacting with {@link DataInputStream} and
668 * {@link DataOutputStream}, mostly dealing with writing partial arrays.
669 */
670 public static class DataStreamUtils {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700671 @Deprecated
672 public static long[] readFullLongArray(DataInputStream in) throws IOException {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700673 final int size = in.readInt();
Jeff Sharkey1f99a482013-07-11 11:18:53 -0700674 if (size < 0) throw new ProtocolException("negative array size");
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700675 final long[] values = new long[size];
676 for (int i = 0; i < values.length; i++) {
677 values[i] = in.readLong();
678 }
679 return values;
Jeff Sharkey75279902011-05-24 18:39:45 -0700680 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700681
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700682 /**
683 * Read variable-length {@link Long} using protobuf-style approach.
684 */
685 public static long readVarLong(DataInputStream in) throws IOException {
686 int shift = 0;
687 long result = 0;
688 while (shift < 64) {
689 byte b = in.readByte();
690 result |= (long) (b & 0x7F) << shift;
691 if ((b & 0x80) == 0)
692 return result;
693 shift += 7;
694 }
695 throw new ProtocolException("malformed long");
696 }
697
698 /**
699 * Write variable-length {@link Long} using protobuf-style approach.
700 */
701 public static void writeVarLong(DataOutputStream out, long value) throws IOException {
702 while (true) {
703 if ((value & ~0x7FL) == 0) {
704 out.writeByte((int) value);
705 return;
706 } else {
707 out.writeByte(((int) value & 0x7F) | 0x80);
708 value >>>= 7;
709 }
710 }
711 }
712
713 public static long[] readVarLongArray(DataInputStream in) throws IOException {
714 final int size = in.readInt();
715 if (size == -1) return null;
Jeff Sharkey1f99a482013-07-11 11:18:53 -0700716 if (size < 0) throw new ProtocolException("negative array size");
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700717 final long[] values = new long[size];
718 for (int i = 0; i < values.length; i++) {
719 values[i] = readVarLong(in);
720 }
721 return values;
722 }
723
724 public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700725 throws IOException {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700726 if (values == null) {
727 out.writeInt(-1);
728 return;
729 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700730 if (size > values.length) {
731 throw new IllegalArgumentException("size larger than length");
732 }
733 out.writeInt(size);
734 for (int i = 0; i < size; i++) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700735 writeVarLong(out, values[i]);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700736 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700737 }
738 }
739
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700740 /**
741 * Utility methods for interacting with {@link Parcel} structures, mostly
742 * dealing with writing partial arrays.
743 */
744 public static class ParcelUtils {
745 public static long[] readLongArray(Parcel in) {
746 final int size = in.readInt();
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700747 if (size == -1) return null;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700748 final long[] values = new long[size];
749 for (int i = 0; i < values.length; i++) {
750 values[i] = in.readLong();
751 }
752 return values;
Jeff Sharkey75279902011-05-24 18:39:45 -0700753 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700754
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700755 public static void writeLongArray(Parcel out, long[] values, int size) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700756 if (values == null) {
757 out.writeInt(-1);
758 return;
759 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700760 if (size > values.length) {
761 throw new IllegalArgumentException("size larger than length");
762 }
763 out.writeInt(size);
764 for (int i = 0; i < size; i++) {
765 out.writeLong(values[i]);
766 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700767 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700768 }
769
770}