blob: d53e0326aeb1d34286ae4e771baff0e2b31e4376 [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 Sharkeyf4de2942017-08-29 15:32:13 -060030
Jeff Sharkey63abc372012-01-11 18:38:16 -080031import static com.android.internal.util.ArrayUtils.total;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070032
Mathew Inwood53f089f2018-08-08 14:44:44 +010033import android.annotation.UnsupportedAppUsage;
Jeff Sharkey75279902011-05-24 18:39:45 -070034import android.os.Parcel;
35import android.os.Parcelable;
Makoto Onukida65a522017-01-13 10:23:30 -080036import android.service.NetworkStatsHistoryBucketProto;
37import android.service.NetworkStatsHistoryProto;
Jeff Sharkey69b0f632011-09-11 17:33:14 -070038import android.util.MathUtils;
Makoto Onukida65a522017-01-13 10:23:30 -080039import android.util.proto.ProtoOutputStream;
Jeff Sharkey75279902011-05-24 18:39:45 -070040
Jeff Sharkey63abc372012-01-11 18:38:16 -080041import com.android.internal.util.IndentingPrintWriter;
42
Jeff Sharkeye0c29952018-02-20 17:24:55 -070043import libcore.util.EmptyArray;
44
Jeff Sharkey75279902011-05-24 18:39:45 -070045import java.io.CharArrayWriter;
46import java.io.DataInputStream;
47import java.io.DataOutputStream;
48import java.io.IOException;
Jeff Sharkey55a442e2014-11-18 18:22:21 -080049import java.io.PrintWriter;
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -070050import java.net.ProtocolException;
Jeff Sharkey75279902011-05-24 18:39:45 -070051import java.util.Arrays;
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -070052import java.util.Random;
Jeff Sharkey75279902011-05-24 18:39:45 -070053
54/**
55 * Collection of historical network statistics, recorded into equally-sized
56 * "buckets" in time. Internally it stores data in {@code long} series for more
57 * efficient persistence.
58 * <p>
59 * Each bucket is defined by a {@link #bucketStart} timestamp, and lasts for
60 * {@link #bucketDuration}. Internally assumes that {@link #bucketStart} is
61 * sorted at all times.
62 *
63 * @hide
64 */
65public class NetworkStatsHistory implements Parcelable {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -070066 private static final int VERSION_INIT = 1;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070067 private static final int VERSION_ADD_PACKETS = 2;
Jeff Sharkey558a2322011-08-24 15:42:09 -070068 private static final int VERSION_ADD_ACTIVE = 3;
Jeff Sharkey75279902011-05-24 18:39:45 -070069
Jeff Sharkey558a2322011-08-24 15:42:09 -070070 public static final int FIELD_ACTIVE_TIME = 0x01;
71 public static final int FIELD_RX_BYTES = 0x02;
72 public static final int FIELD_RX_PACKETS = 0x04;
73 public static final int FIELD_TX_BYTES = 0x08;
74 public static final int FIELD_TX_PACKETS = 0x10;
75 public static final int FIELD_OPERATIONS = 0x20;
Jeff Sharkey75279902011-05-24 18:39:45 -070076
Jeff Sharkey63d27a92011-08-03 17:04:22 -070077 public static final int FIELD_ALL = 0xFFFFFFFF;
78
79 private long bucketDuration;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070080 private int bucketCount;
81 private long[] bucketStart;
Jeff Sharkey558a2322011-08-24 15:42:09 -070082 private long[] activeTime;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070083 private long[] rxBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070084 private long[] rxPackets;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070085 private long[] txBytes;
Jeff Sharkeya63ba592011-07-19 23:47:12 -070086 private long[] txPackets;
Jeff Sharkey63d27a92011-08-03 17:04:22 -070087 private long[] operations;
Jeff Sharkey63abc372012-01-11 18:38:16 -080088 private long totalBytes;
Jeff Sharkey75279902011-05-24 18:39:45 -070089
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070090 public static class Entry {
Jeff Sharkey63d27a92011-08-03 17:04:22 -070091 public static final long UNKNOWN = -1;
92
Mathew Inwood53f089f2018-08-08 14:44:44 +010093 @UnsupportedAppUsage
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070094 public long bucketDuration;
Mathew Inwood53f089f2018-08-08 14:44:44 +010095 @UnsupportedAppUsage
Jeff Sharkey558a2322011-08-24 15:42:09 -070096 public long bucketStart;
97 public long activeTime;
Mathew Inwood53f089f2018-08-08 14:44:44 +010098 @UnsupportedAppUsage
Jeff Sharkeyd37948f2011-07-12 13:57:00 -070099 public long rxBytes;
Mathew Inwood53f089f2018-08-08 14:44:44 +0100100 @UnsupportedAppUsage
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700101 public long rxPackets;
Mathew Inwood53f089f2018-08-08 14:44:44 +0100102 @UnsupportedAppUsage
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700103 public long txBytes;
Mathew Inwood53f089f2018-08-08 14:44:44 +0100104 @UnsupportedAppUsage
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700105 public long txPackets;
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700106 public long operations;
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700107 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700108
Mathew Inwood53f089f2018-08-08 14:44:44 +0100109 @UnsupportedAppUsage
Jeff Sharkeyd2a45872011-05-28 20:56:34 -0700110 public NetworkStatsHistory(long bucketDuration) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700111 this(bucketDuration, 10, FIELD_ALL);
Jeff Sharkey4a971222011-06-11 22:16:55 -0700112 }
113
114 public NetworkStatsHistory(long bucketDuration, int initialSize) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700115 this(bucketDuration, initialSize, FIELD_ALL);
116 }
117
118 public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700119 this.bucketDuration = bucketDuration;
Jeff Sharkey4a971222011-06-11 22:16:55 -0700120 bucketStart = new long[initialSize];
Jeff Sharkey558a2322011-08-24 15:42:09 -0700121 if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700122 if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
123 if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
124 if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
125 if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
126 if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
Jeff Sharkey4a971222011-06-11 22:16:55 -0700127 bucketCount = 0;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800128 totalBytes = 0;
129 }
130
131 public NetworkStatsHistory(NetworkStatsHistory existing, long bucketDuration) {
132 this(bucketDuration, existing.estimateResizeBuckets(bucketDuration));
133 recordEntireHistory(existing);
Jeff Sharkey75279902011-05-24 18:39:45 -0700134 }
135
Mathew Inwood53f089f2018-08-08 14:44:44 +0100136 @UnsupportedAppUsage
Jeff Sharkey75279902011-05-24 18:39:45 -0700137 public NetworkStatsHistory(Parcel in) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700138 bucketDuration = in.readLong();
139 bucketStart = readLongArray(in);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700140 activeTime = readLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700141 rxBytes = readLongArray(in);
142 rxPackets = readLongArray(in);
143 txBytes = readLongArray(in);
144 txPackets = readLongArray(in);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700145 operations = readLongArray(in);
Jeff Sharkey75279902011-05-24 18:39:45 -0700146 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800147 totalBytes = in.readLong();
Jeff Sharkey75279902011-05-24 18:39:45 -0700148 }
149
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700150 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700151 public void writeToParcel(Parcel out, int flags) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700152 out.writeLong(bucketDuration);
153 writeLongArray(out, bucketStart, bucketCount);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700154 writeLongArray(out, activeTime, bucketCount);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700155 writeLongArray(out, rxBytes, bucketCount);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700156 writeLongArray(out, rxPackets, bucketCount);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700157 writeLongArray(out, txBytes, bucketCount);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700158 writeLongArray(out, txPackets, bucketCount);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700159 writeLongArray(out, operations, bucketCount);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800160 out.writeLong(totalBytes);
Jeff Sharkey75279902011-05-24 18:39:45 -0700161 }
162
163 public NetworkStatsHistory(DataInputStream in) throws IOException {
164 final int version = in.readInt();
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700165 switch (version) {
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700166 case VERSION_INIT: {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700167 bucketDuration = in.readLong();
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700168 bucketStart = readFullLongArray(in);
169 rxBytes = readFullLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700170 rxPackets = new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700171 txBytes = readFullLongArray(in);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700172 txPackets = new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700173 operations = new long[bucketStart.length];
174 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800175 totalBytes = total(rxBytes) + total(txBytes);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700176 break;
177 }
Jeff Sharkey558a2322011-08-24 15:42:09 -0700178 case VERSION_ADD_PACKETS:
179 case VERSION_ADD_ACTIVE: {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700180 bucketDuration = in.readLong();
181 bucketStart = readVarLongArray(in);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700182 activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in)
183 : new long[bucketStart.length];
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700184 rxBytes = readVarLongArray(in);
185 rxPackets = readVarLongArray(in);
186 txBytes = readVarLongArray(in);
187 txPackets = readVarLongArray(in);
188 operations = readVarLongArray(in);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700189 bucketCount = bucketStart.length;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800190 totalBytes = total(rxBytes) + total(txBytes);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700191 break;
192 }
193 default: {
194 throw new ProtocolException("unexpected version: " + version);
195 }
196 }
Jeff Sharkeyb0a579f2012-11-02 14:48:20 -0700197
198 if (bucketStart.length != bucketCount || rxBytes.length != bucketCount
199 || rxPackets.length != bucketCount || txBytes.length != bucketCount
200 || txPackets.length != bucketCount || operations.length != bucketCount) {
201 throw new ProtocolException("Mismatched history lengths");
202 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700203 }
204
205 public void writeToStream(DataOutputStream out) throws IOException {
Jeff Sharkey558a2322011-08-24 15:42:09 -0700206 out.writeInt(VERSION_ADD_ACTIVE);
Jeff Sharkey75279902011-05-24 18:39:45 -0700207 out.writeLong(bucketDuration);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700208 writeVarLongArray(out, bucketStart, bucketCount);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700209 writeVarLongArray(out, activeTime, bucketCount);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700210 writeVarLongArray(out, rxBytes, bucketCount);
211 writeVarLongArray(out, rxPackets, bucketCount);
212 writeVarLongArray(out, txBytes, bucketCount);
213 writeVarLongArray(out, txPackets, bucketCount);
214 writeVarLongArray(out, operations, bucketCount);
Jeff Sharkey75279902011-05-24 18:39:45 -0700215 }
216
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700217 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700218 public int describeContents() {
219 return 0;
220 }
221
Mathew Inwood53f089f2018-08-08 14:44:44 +0100222 @UnsupportedAppUsage
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700223 public int size() {
224 return bucketCount;
225 }
226
227 public long getBucketDuration() {
228 return bucketDuration;
229 }
230
Mathew Inwood53f089f2018-08-08 14:44:44 +0100231 @UnsupportedAppUsage
Jeff Sharkey434962e2011-07-12 20:20:56 -0700232 public long getStart() {
233 if (bucketCount > 0) {
234 return bucketStart[0];
235 } else {
236 return Long.MAX_VALUE;
237 }
238 }
239
Mathew Inwood53f089f2018-08-08 14:44:44 +0100240 @UnsupportedAppUsage
Jeff Sharkey434962e2011-07-12 20:20:56 -0700241 public long getEnd() {
242 if (bucketCount > 0) {
243 return bucketStart[bucketCount - 1] + bucketDuration;
244 } else {
245 return Long.MIN_VALUE;
246 }
247 }
248
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700249 /**
Jeff Sharkey63abc372012-01-11 18:38:16 -0800250 * Return total bytes represented by this history.
251 */
252 public long getTotalBytes() {
253 return totalBytes;
254 }
255
256 /**
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700257 * Return index of bucket that contains or is immediately before the
258 * requested time.
259 */
Mathew Inwood53f089f2018-08-08 14:44:44 +0100260 @UnsupportedAppUsage
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700261 public int getIndexBefore(long time) {
262 int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
263 if (index < 0) {
264 index = (~index) - 1;
265 } else {
266 index -= 1;
267 }
268 return MathUtils.constrain(index, 0, bucketCount - 1);
269 }
270
271 /**
272 * Return index of bucket that contains or is immediately after the
273 * requested time.
274 */
275 public int getIndexAfter(long time) {
276 int index = Arrays.binarySearch(bucketStart, 0, bucketCount, time);
277 if (index < 0) {
278 index = ~index;
279 } else {
280 index += 1;
281 }
282 return MathUtils.constrain(index, 0, bucketCount - 1);
283 }
284
285 /**
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700286 * Return specific stats entry.
287 */
Mathew Inwood53f089f2018-08-08 14:44:44 +0100288 @UnsupportedAppUsage
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700289 public Entry getValues(int i, Entry recycle) {
290 final Entry entry = recycle != null ? recycle : new Entry();
291 entry.bucketStart = bucketStart[i];
292 entry.bucketDuration = bucketDuration;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700293 entry.activeTime = getLong(activeTime, i, UNKNOWN);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700294 entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
295 entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
296 entry.txBytes = getLong(txBytes, i, UNKNOWN);
297 entry.txPackets = getLong(txPackets, i, UNKNOWN);
298 entry.operations = getLong(operations, i, UNKNOWN);
Jeff Sharkeyd37948f2011-07-12 13:57:00 -0700299 return entry;
300 }
301
Jeff Sharkeyf4de2942017-08-29 15:32:13 -0600302 public void setValues(int i, Entry entry) {
303 // Unwind old values
304 if (rxBytes != null) totalBytes -= rxBytes[i];
305 if (txBytes != null) totalBytes -= txBytes[i];
306
307 bucketStart[i] = entry.bucketStart;
308 setLong(activeTime, i, entry.activeTime);
309 setLong(rxBytes, i, entry.rxBytes);
310 setLong(rxPackets, i, entry.rxPackets);
311 setLong(txBytes, i, entry.txBytes);
312 setLong(txPackets, i, entry.txPackets);
313 setLong(operations, i, entry.operations);
314
315 // Apply new values
316 if (rxBytes != null) totalBytes += rxBytes[i];
317 if (txBytes != null) totalBytes += txBytes[i];
318 }
319
Jeff Sharkey75279902011-05-24 18:39:45 -0700320 /**
321 * Record that data traffic occurred in the given time range. Will
322 * distribute across internal buckets, creating new buckets as needed.
323 */
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700324 @Deprecated
325 public void recordData(long start, long end, long rxBytes, long txBytes) {
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700326 recordData(start, end, new NetworkStats.Entry(
327 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700328 }
329
330 /**
331 * Record that data traffic occurred in the given time range. Will
332 * distribute across internal buckets, creating new buckets as needed.
333 */
334 public void recordData(long start, long end, NetworkStats.Entry entry) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800335 long rxBytes = entry.rxBytes;
336 long rxPackets = entry.rxPackets;
337 long txBytes = entry.txBytes;
338 long txPackets = entry.txPackets;
339 long operations = entry.operations;
340
341 if (entry.isNegative()) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700342 throw new IllegalArgumentException("tried recording negative data");
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700343 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800344 if (entry.isEmpty()) {
Jeff Sharkey367d15a2011-09-22 14:59:51 -0700345 return;
346 }
Jeff Sharkey1b5a2a92011-06-18 18:34:16 -0700347
Jeff Sharkey75279902011-05-24 18:39:45 -0700348 // create any buckets needed by this range
349 ensureBuckets(start, end);
350
351 // distribute data usage into buckets
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700352 long duration = end - start;
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700353 final int startIndex = getIndexAfter(end);
354 for (int i = startIndex; i >= 0; i--) {
Jeff Sharkey75279902011-05-24 18:39:45 -0700355 final long curStart = bucketStart[i];
356 final long curEnd = curStart + bucketDuration;
357
358 // bucket is older than record; we're finished
359 if (curEnd < start) break;
360 // bucket is newer than record; keep looking
361 if (curStart > end) continue;
362
363 final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700364 if (overlap <= 0) continue;
365
366 // integer math each time is faster than floating point
Jeff Sharkey63abc372012-01-11 18:38:16 -0800367 final long fracRxBytes = rxBytes * overlap / duration;
368 final long fracRxPackets = rxPackets * overlap / duration;
369 final long fracTxBytes = txBytes * overlap / duration;
370 final long fracTxPackets = txPackets * overlap / duration;
371 final long fracOperations = operations * overlap / duration;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700372
Jeff Sharkey558a2322011-08-24 15:42:09 -0700373 addLong(activeTime, i, overlap);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800374 addLong(this.rxBytes, i, fracRxBytes); rxBytes -= fracRxBytes;
375 addLong(this.rxPackets, i, fracRxPackets); rxPackets -= fracRxPackets;
376 addLong(this.txBytes, i, fracTxBytes); txBytes -= fracTxBytes;
377 addLong(this.txPackets, i, fracTxPackets); txPackets -= fracTxPackets;
378 addLong(this.operations, i, fracOperations); operations -= fracOperations;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700379
380 duration -= overlap;
Jeff Sharkey75279902011-05-24 18:39:45 -0700381 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800382
383 totalBytes += entry.rxBytes + entry.txBytes;
Jeff Sharkey75279902011-05-24 18:39:45 -0700384 }
385
386 /**
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700387 * Record an entire {@link NetworkStatsHistory} into this history. Usually
388 * for combining together stats for external reporting.
389 */
Mathew Inwood53f089f2018-08-08 14:44:44 +0100390 @UnsupportedAppUsage
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700391 public void recordEntireHistory(NetworkStatsHistory input) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700392 recordHistory(input, Long.MIN_VALUE, Long.MAX_VALUE);
393 }
394
395 /**
396 * Record given {@link NetworkStatsHistory} into this history, copying only
397 * buckets that atomically occur in the inclusive time range. Doesn't
398 * interpolate across partial buckets.
399 */
400 public void recordHistory(NetworkStatsHistory input, long start, long end) {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700401 final NetworkStats.Entry entry = new NetworkStats.Entry(
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700402 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700403 for (int i = 0; i < input.bucketCount; i++) {
Jeff Sharkey70c70532012-05-16 14:51:19 -0700404 final long bucketStart = input.bucketStart[i];
405 final long bucketEnd = bucketStart + input.bucketDuration;
406
407 // skip when bucket is outside requested range
408 if (bucketStart < start || bucketEnd > end) continue;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700409
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700410 entry.rxBytes = getLong(input.rxBytes, i, 0L);
411 entry.rxPackets = getLong(input.rxPackets, i, 0L);
412 entry.txBytes = getLong(input.txBytes, i, 0L);
413 entry.txPackets = getLong(input.txPackets, i, 0L);
414 entry.operations = getLong(input.operations, i, 0L);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700415
Jeff Sharkey70c70532012-05-16 14:51:19 -0700416 recordData(bucketStart, bucketEnd, entry);
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700417 }
418 }
419
420 /**
Jeff Sharkey75279902011-05-24 18:39:45 -0700421 * Ensure that buckets exist for given time range, creating as needed.
422 */
423 private void ensureBuckets(long start, long end) {
424 // normalize incoming range to bucket boundaries
425 start -= start % bucketDuration;
426 end += (bucketDuration - (end % bucketDuration)) % bucketDuration;
427
428 for (long now = start; now < end; now += bucketDuration) {
429 // try finding existing bucket
430 final int index = Arrays.binarySearch(bucketStart, 0, bucketCount, now);
431 if (index < 0) {
432 // bucket missing, create and insert
433 insertBucket(~index, now);
434 }
435 }
436 }
437
438 /**
439 * Insert new bucket at requested index and starting time.
440 */
441 private void insertBucket(int index, long start) {
442 // create more buckets when needed
Jeff Sharkey4a971222011-06-11 22:16:55 -0700443 if (bucketCount >= bucketStart.length) {
444 final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
Jeff Sharkey75279902011-05-24 18:39:45 -0700445 bucketStart = Arrays.copyOf(bucketStart, newLength);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700446 if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700447 if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
448 if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
449 if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
450 if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength);
451 if (operations != null) operations = Arrays.copyOf(operations, newLength);
Jeff Sharkey75279902011-05-24 18:39:45 -0700452 }
453
454 // create gap when inserting bucket in middle
455 if (index < bucketCount) {
456 final int dstPos = index + 1;
457 final int length = bucketCount - index;
458
459 System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700460 if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700461 if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
462 if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
463 if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
464 if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length);
465 if (operations != null) System.arraycopy(operations, index, operations, dstPos, length);
Jeff Sharkey75279902011-05-24 18:39:45 -0700466 }
467
468 bucketStart[index] = start;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700469 setLong(activeTime, index, 0L);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700470 setLong(rxBytes, index, 0L);
471 setLong(rxPackets, index, 0L);
472 setLong(txBytes, index, 0L);
473 setLong(txPackets, index, 0L);
474 setLong(operations, index, 0L);
Jeff Sharkey75279902011-05-24 18:39:45 -0700475 bucketCount++;
476 }
477
478 /**
Jeff Sharkeye0c29952018-02-20 17:24:55 -0700479 * Clear all data stored in this object.
480 */
481 public void clear() {
482 bucketStart = EmptyArray.LONG;
483 if (activeTime != null) activeTime = EmptyArray.LONG;
484 if (rxBytes != null) rxBytes = EmptyArray.LONG;
485 if (rxPackets != null) rxPackets = EmptyArray.LONG;
486 if (txBytes != null) txBytes = EmptyArray.LONG;
487 if (txPackets != null) txPackets = EmptyArray.LONG;
488 if (operations != null) operations = EmptyArray.LONG;
489 bucketCount = 0;
490 totalBytes = 0;
491 }
492
493 /**
Jeff Sharkey75279902011-05-24 18:39:45 -0700494 * Remove buckets older than requested cutoff.
495 */
Jeff Sharkey63abc372012-01-11 18:38:16 -0800496 @Deprecated
Jeff Sharkey75279902011-05-24 18:39:45 -0700497 public void removeBucketsBefore(long cutoff) {
498 int i;
499 for (i = 0; i < bucketCount; i++) {
500 final long curStart = bucketStart[i];
501 final long curEnd = curStart + bucketDuration;
502
503 // cutoff happens before or during this bucket; everything before
504 // this bucket should be removed.
505 if (curEnd > cutoff) break;
506 }
507
508 if (i > 0) {
509 final int length = bucketStart.length;
510 bucketStart = Arrays.copyOfRange(bucketStart, i, length);
Jeff Sharkey558a2322011-08-24 15:42:09 -0700511 if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700512 if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
513 if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
514 if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
515 if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
516 if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
Jeff Sharkey75279902011-05-24 18:39:45 -0700517 bucketCount -= i;
Jeff Sharkey63abc372012-01-11 18:38:16 -0800518
519 // TODO: subtract removed values from totalBytes
Jeff Sharkey75279902011-05-24 18:39:45 -0700520 }
521 }
522
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700523 /**
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700524 * Return interpolated data usage across the requested range. Interpolates
525 * across buckets, so values may be rounded slightly.
526 */
Mathew Inwood53f089f2018-08-08 14:44:44 +0100527 @UnsupportedAppUsage
Jeff Sharkey434962e2011-07-12 20:20:56 -0700528 public Entry getValues(long start, long end, Entry recycle) {
529 return getValues(start, end, Long.MAX_VALUE, recycle);
530 }
531
532 /**
533 * Return interpolated data usage across the requested range. Interpolates
534 * across buckets, so values may be rounded slightly.
535 */
Mathew Inwood53f089f2018-08-08 14:44:44 +0100536 @UnsupportedAppUsage
Jeff Sharkey434962e2011-07-12 20:20:56 -0700537 public Entry getValues(long start, long end, long now, Entry recycle) {
538 final Entry entry = recycle != null ? recycle : new Entry();
Jeff Sharkey434962e2011-07-12 20:20:56 -0700539 entry.bucketDuration = end - start;
Jeff Sharkey558a2322011-08-24 15:42:09 -0700540 entry.bucketStart = start;
541 entry.activeTime = activeTime != null ? 0 : UNKNOWN;
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700542 entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
543 entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
544 entry.txBytes = txBytes != null ? 0 : UNKNOWN;
545 entry.txPackets = txPackets != null ? 0 : UNKNOWN;
546 entry.operations = operations != null ? 0 : UNKNOWN;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700547
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700548 final int startIndex = getIndexAfter(end);
549 for (int i = startIndex; i >= 0; i--) {
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700550 final long curStart = bucketStart[i];
551 final long curEnd = curStart + bucketDuration;
552
Jeff Sharkey34c73ac2011-09-18 13:30:23 -0700553 // bucket is older than request; we're finished
554 if (curEnd <= start) break;
555 // bucket is newer than request; keep looking
556 if (curStart >= end) continue;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700557
Jeff Sharkey434962e2011-07-12 20:20:56 -0700558 // include full value for active buckets, otherwise only fractional
559 final boolean activeBucket = curStart < now && curEnd > now;
Jeff Sharkey69b0f632011-09-11 17:33:14 -0700560 final long overlap;
561 if (activeBucket) {
562 overlap = bucketDuration;
563 } else {
564 final long overlapEnd = curEnd < end ? curEnd : end;
565 final long overlapStart = curStart > start ? curStart : start;
566 overlap = overlapEnd - overlapStart;
567 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700568 if (overlap <= 0) continue;
569
570 // integer math each time is faster than floating point
Jeff Sharkey558a2322011-08-24 15:42:09 -0700571 if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration;
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700572 if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
573 if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
574 if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
575 if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
576 if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700577 }
Jeff Sharkey434962e2011-07-12 20:20:56 -0700578 return entry;
Jeff Sharkey19862bf2011-06-02 17:38:22 -0700579 }
580
581 /**
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700582 * @deprecated only for temporary testing
583 */
584 @Deprecated
Jeff Sharkey293779f2011-10-05 23:31:57 -0700585 public void generateRandom(long start, long end, long bytes) {
586 final Random r = new Random();
587
588 final float fractionRx = r.nextFloat();
589 final long rxBytes = (long) (bytes * fractionRx);
590 final long txBytes = (long) (bytes * (1 - fractionRx));
591
592 final long rxPackets = rxBytes / 1024;
593 final long txPackets = txBytes / 1024;
594 final long operations = rxBytes / 2048;
595
596 generateRandom(start, end, rxBytes, rxPackets, txBytes, txPackets, operations, r);
597 }
598
599 /**
600 * @deprecated only for temporary testing
601 */
602 @Deprecated
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700603 public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
Jeff Sharkey293779f2011-10-05 23:31:57 -0700604 long txPackets, long operations, Random r) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700605 ensureBuckets(start, end);
606
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700607 final NetworkStats.Entry entry = new NetworkStats.Entry(
Jeff Sharkeyb5d55e32011-08-10 17:53:27 -0700608 IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
Jeff Sharkey04cd0e42011-08-05 11:09:46 -0700609 while (rxBytes > 1024 || rxPackets > 128 || txBytes > 1024 || txPackets > 128
610 || operations > 32) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700611 final long curStart = randomLong(r, start, end);
Jeff Sharkey293779f2011-10-05 23:31:57 -0700612 final long curEnd = curStart + randomLong(r, 0, (end - curStart) / 2);
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700613
614 entry.rxBytes = randomLong(r, 0, rxBytes);
615 entry.rxPackets = randomLong(r, 0, rxPackets);
616 entry.txBytes = randomLong(r, 0, txBytes);
617 entry.txPackets = randomLong(r, 0, txPackets);
618 entry.operations = randomLong(r, 0, operations);
619
620 rxBytes -= entry.rxBytes;
621 rxPackets -= entry.rxPackets;
622 txBytes -= entry.txBytes;
623 txPackets -= entry.txPackets;
624 operations -= entry.operations;
Jeff Sharkeyf0ceede2011-08-02 17:22:34 -0700625
626 recordData(curStart, curEnd, entry);
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700627 }
628 }
629
Jeff Sharkey293779f2011-10-05 23:31:57 -0700630 public static long randomLong(Random r, long start, long end) {
Jeff Sharkey61ee0bb2011-05-29 22:50:42 -0700631 return (long) (start + (r.nextFloat() * (end - start)));
632 }
633
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800634 /**
635 * Quickly determine if this history intersects with given window.
636 */
637 public boolean intersects(long start, long end) {
638 final long dataStart = getStart();
639 final long dataEnd = getEnd();
640 if (start >= dataStart && start <= dataEnd) return true;
641 if (end >= dataStart && end <= dataEnd) return true;
642 if (dataStart >= start && dataStart <= end) return true;
643 if (dataEnd >= start && dataEnd <= end) return true;
644 return false;
645 }
646
Jeff Sharkey63abc372012-01-11 18:38:16 -0800647 public void dump(IndentingPrintWriter pw, boolean fullHistory) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800648 pw.print("NetworkStatsHistory: bucketDuration=");
649 pw.println(bucketDuration / SECOND_IN_MILLIS);
Jeff Sharkey63abc372012-01-11 18:38:16 -0800650 pw.increaseIndent();
Jeff Sharkey350083e2011-06-29 10:45:16 -0700651
652 final int start = fullHistory ? 0 : Math.max(0, bucketCount - 32);
653 if (start > 0) {
Jeff Sharkey63abc372012-01-11 18:38:16 -0800654 pw.print("(omitting "); pw.print(start); pw.println(" buckets)");
Jeff Sharkey350083e2011-06-29 10:45:16 -0700655 }
656
657 for (int i = start; i < bucketCount; i++) {
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800658 pw.print("st="); pw.print(bucketStart[i] / SECOND_IN_MILLIS);
659 if (rxBytes != null) { pw.print(" rb="); pw.print(rxBytes[i]); }
660 if (rxPackets != null) { pw.print(" rp="); pw.print(rxPackets[i]); }
661 if (txBytes != null) { pw.print(" tb="); pw.print(txBytes[i]); }
662 if (txPackets != null) { pw.print(" tp="); pw.print(txPackets[i]); }
663 if (operations != null) { pw.print(" op="); pw.print(operations[i]); }
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700664 pw.println();
Jeff Sharkey75279902011-05-24 18:39:45 -0700665 }
Jeff Sharkey63abc372012-01-11 18:38:16 -0800666
667 pw.decreaseIndent();
Jeff Sharkey75279902011-05-24 18:39:45 -0700668 }
669
Jeff Sharkey55a442e2014-11-18 18:22:21 -0800670 public void dumpCheckin(PrintWriter pw) {
671 pw.print("d,");
672 pw.print(bucketDuration / SECOND_IN_MILLIS);
673 pw.println();
674
675 for (int i = 0; i < bucketCount; i++) {
676 pw.print("b,");
677 pw.print(bucketStart[i] / SECOND_IN_MILLIS); pw.print(',');
678 if (rxBytes != null) { pw.print(rxBytes[i]); } else { pw.print("*"); } pw.print(',');
679 if (rxPackets != null) { pw.print(rxPackets[i]); } else { pw.print("*"); } pw.print(',');
680 if (txBytes != null) { pw.print(txBytes[i]); } else { pw.print("*"); } pw.print(',');
681 if (txPackets != null) { pw.print(txPackets[i]); } else { pw.print("*"); } pw.print(',');
682 if (operations != null) { pw.print(operations[i]); } else { pw.print("*"); }
683 pw.println();
684 }
685 }
686
Makoto Onukida65a522017-01-13 10:23:30 -0800687 public void writeToProto(ProtoOutputStream proto, long tag) {
688 final long start = proto.start(tag);
689
690 proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
691
692 for (int i = 0; i < bucketCount; i++) {
693 final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
694
695 proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
696 writeToProto(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
697 writeToProto(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
698 writeToProto(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
699 writeToProto(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
700 writeToProto(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
701
702 proto.end(startBucket);
703 }
704
705 proto.end(start);
706 }
707
708 private static void writeToProto(ProtoOutputStream proto, long tag, long[] array, int index) {
709 if (array != null) {
710 proto.write(tag, array[index]);
711 }
712 }
713
Jeff Sharkey75279902011-05-24 18:39:45 -0700714 @Override
715 public String toString() {
716 final CharArrayWriter writer = new CharArrayWriter();
Jeff Sharkey63abc372012-01-11 18:38:16 -0800717 dump(new IndentingPrintWriter(writer, " "), false);
Jeff Sharkey75279902011-05-24 18:39:45 -0700718 return writer.toString();
719 }
720
Mathew Inwood53f089f2018-08-08 14:44:44 +0100721 @UnsupportedAppUsage
Jeff Sharkey75279902011-05-24 18:39:45 -0700722 public static final Creator<NetworkStatsHistory> CREATOR = new Creator<NetworkStatsHistory>() {
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700723 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700724 public NetworkStatsHistory createFromParcel(Parcel in) {
725 return new NetworkStatsHistory(in);
726 }
727
Jeff Sharkeybfdd6802012-04-09 10:49:19 -0700728 @Override
Jeff Sharkey75279902011-05-24 18:39:45 -0700729 public NetworkStatsHistory[] newArray(int size) {
730 return new NetworkStatsHistory[size];
731 }
732 };
733
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700734 private static long getLong(long[] array, int i, long value) {
735 return array != null ? array[i] : value;
736 }
737
738 private static void setLong(long[] array, int i, long value) {
739 if (array != null) array[i] = value;
740 }
741
742 private static void addLong(long[] array, int i, long value) {
743 if (array != null) array[i] += value;
744 }
745
Jeff Sharkey63abc372012-01-11 18:38:16 -0800746 public int estimateResizeBuckets(long newBucketDuration) {
747 return (int) (size() * getBucketDuration() / newBucketDuration);
748 }
749
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700750 /**
751 * Utility methods for interacting with {@link DataInputStream} and
752 * {@link DataOutputStream}, mostly dealing with writing partial arrays.
753 */
754 public static class DataStreamUtils {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700755 @Deprecated
756 public static long[] readFullLongArray(DataInputStream in) throws IOException {
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700757 final int size = in.readInt();
Jeff Sharkey1f99a482013-07-11 11:18:53 -0700758 if (size < 0) throw new ProtocolException("negative array size");
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700759 final long[] values = new long[size];
760 for (int i = 0; i < values.length; i++) {
761 values[i] = in.readLong();
762 }
763 return values;
Jeff Sharkey75279902011-05-24 18:39:45 -0700764 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700765
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700766 /**
767 * Read variable-length {@link Long} using protobuf-style approach.
768 */
769 public static long readVarLong(DataInputStream in) throws IOException {
770 int shift = 0;
771 long result = 0;
772 while (shift < 64) {
773 byte b = in.readByte();
774 result |= (long) (b & 0x7F) << shift;
775 if ((b & 0x80) == 0)
776 return result;
777 shift += 7;
778 }
779 throw new ProtocolException("malformed long");
780 }
781
782 /**
783 * Write variable-length {@link Long} using protobuf-style approach.
784 */
785 public static void writeVarLong(DataOutputStream out, long value) throws IOException {
786 while (true) {
787 if ((value & ~0x7FL) == 0) {
788 out.writeByte((int) value);
789 return;
790 } else {
791 out.writeByte(((int) value & 0x7F) | 0x80);
792 value >>>= 7;
793 }
794 }
795 }
796
797 public static long[] readVarLongArray(DataInputStream in) throws IOException {
798 final int size = in.readInt();
799 if (size == -1) return null;
Jeff Sharkey1f99a482013-07-11 11:18:53 -0700800 if (size < 0) throw new ProtocolException("negative array size");
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700801 final long[] values = new long[size];
802 for (int i = 0; i < values.length; i++) {
803 values[i] = readVarLong(in);
804 }
805 return values;
806 }
807
808 public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700809 throws IOException {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700810 if (values == null) {
811 out.writeInt(-1);
812 return;
813 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700814 if (size > values.length) {
815 throw new IllegalArgumentException("size larger than length");
816 }
817 out.writeInt(size);
818 for (int i = 0; i < size; i++) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700819 writeVarLong(out, values[i]);
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700820 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700821 }
822 }
823
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700824 /**
825 * Utility methods for interacting with {@link Parcel} structures, mostly
826 * dealing with writing partial arrays.
827 */
828 public static class ParcelUtils {
829 public static long[] readLongArray(Parcel in) {
830 final int size = in.readInt();
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700831 if (size == -1) return null;
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700832 final long[] values = new long[size];
833 for (int i = 0; i < values.length; i++) {
834 values[i] = in.readLong();
835 }
836 return values;
Jeff Sharkey75279902011-05-24 18:39:45 -0700837 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700838
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700839 public static void writeLongArray(Parcel out, long[] values, int size) {
Jeff Sharkey63d27a92011-08-03 17:04:22 -0700840 if (values == null) {
841 out.writeInt(-1);
842 return;
843 }
Jeff Sharkeya63ba592011-07-19 23:47:12 -0700844 if (size > values.length) {
845 throw new IllegalArgumentException("size larger than length");
846 }
847 out.writeInt(size);
848 for (int i = 0; i < size; i++) {
849 out.writeLong(values[i]);
850 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700851 }
Jeff Sharkey75279902011-05-24 18:39:45 -0700852 }
853
854}